如何避免简单的递归模板typedef [英] How to avoid simple recursive template typedefs

查看:151
本文介绍了如何避免简单的递归模板typedef的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下简单的问题:
一个类 template< typename D> Parser< Parser> 定义 ModuleType 的Parser 。我想将解析器类型注入到模块中,以便能够从解析器中再次提取几种类型。这很方便,因为模块中只需要一个模板参数。但是如果解析器需要在模块中定义一些类型,例如 OptionsType ,在 Parser 通过使用声明使用ModuleOptions = ... 显然不适用于派生类 ParserDerived 的倾向。错误:错误:没有在'struct ParserDerived< double>'中使用DType = typename Parser :: DType;
$ b

我害怕使用这种模式,因为我可能会在未来意识到,所有我的这些模式的构造崩溃成吨的难以理解的编译器失败...



对于下面的问题,什么是更好的方法?



CODE

  #include< iostream> 
#include< type_traits>

using namespace std;

template< typename Parser>
struct Module {
using DType = typename Parser :: DType;
using OptionsType = int;
};

template< typename D,typename Derived = void>
struct Parser {
使用DType = D;

使用DerivedType = typename std :: conditional< std :: is_same< Derived,void> :: value,Parser,Derived> :: type;
using ModuleType = Module< DerivedType> ;;
// using ModuleOptions = typename ModuleType :: OptionsType; //取消注释!
};

template< typename D>
struct ParserDerived:Parser< D,ParserDerived< D> > {
using Base = Parser< D,ParserDerived< D> > ;;

using ModuleType = typename Base :: ModuleType;
using DType = typename Base :: DType;
};


int main(){
Parser< double> t;

ParserDerived< double> d;
}


解决方案

p>


  • d 定义为 ParserDerived< double> ,以便实例化

    • 基类被赋值为 Parser ,以便实例化

      • DType 定义为 double

      • DerivedType 定义为 ParserDerived< double>

      • ModuleType 定义为 Module< ParserDerived< double>>

      • ModuleOptions 定义为 Module< ParserDerived< double>> :: OptionsType 模块< ParserDerived< double>> 实例化

        • DType ParserDerived< double> :: DType 错误 li> OptionsType 定义为 int



    • 基本定义为 Parser< double,ParserDerived< double>> ; c>

    • ModuleType 定义为 Parser< double,ParserDerived< double> > :: ModuleType

    • DType 定义为 Parser < ParserDerived< double>> :: DType




如果你画出这样的实例化,很明显,在定义之前使用 DType 。这不是立即显而易见的模板实例化必须按顺序执行,像这样,但dyp的评论你的问题已经回答,这是一个有效的模板实例化的手段,你可以看到它是多个编译器做的。



您必须重新设计您的设计。在这种特殊情况下,我认为一个非常可行的方法是模仿标准库(一点)并提供一个解析器traits类。您将移动 ModuleType DType 的定义,以便访问这些定义不需要实例化解析器类。



回应您的评论:



无论您是否评论派生类的 DType ,因为无论它是否被定义都无法看到,但它是一个很好的问题为什么基类的 DType 没有使用在它的位置。 Parser 正在被实例化,以便将其用作基类,但在该实例化期间,它不被视为基类。在实例化之后,编译器首先确保 Parser 适合作为基类,



对于更清楚地表明这一点的更短的例子:

  template< class B> struct A {
static void f(A&);
static decltype(f(*(B *)0))g();
};
struct B:A< B> {};

由于 B 当传递一个左值时,应该可以调用A< B> A< B&键入 B 。但是,这并不能阻止编译器抱怨 g 的声明,并且clang的错误消息相当明确地调用 A< B> B 无关类型:


错误:对类型A< B>的引用不能绑定到无关类型'B'的值


c $ c> B 之后只有 A c> A< B> 已完成。


I have the following simple problem: A class template<typename D> Parser which defines a ModuleType as Module<Parser>. I would like to inject the parser type into the module, as to be able to extract again several types from the parser in it. This is handy as one needs only one template parameter in Module. But the problem comes if the parser needs some types which are defined in the module such as OptionsType, accessing this in the Parser by the using declaration using ModuleOptions = ... obviously does not work for an istantiation of the derived class ParserDerived. Error: error: no type named ‘DType’ in ‘struct ParserDerived<double>’ using DType = typename Parser::DType; So somehow the types

I am afraid of using such patterns and because I might be realising in the future that all my construction with these patterns collapse into tons of hard to understand compiler failures...

What would be a better approach for the problem below?

CODE

#include <iostream>
#include <type_traits>

using namespace std;

template<typename Parser>
struct Module{
    using DType = typename Parser::DType;
    using OptionsType = int;
};

template<typename D, typename Derived = void >
struct Parser{
    using DType = D;

    using DerivedType = typename std::conditional< std::is_same<Derived,void>::value, Parser, Derived>::type;
    using ModuleType = Module<DerivedType>;
    //using ModuleOptions = typename ModuleType::OptionsType; //uncomment this!!
};

template<typename D>
struct ParserDerived: Parser<D, ParserDerived<D> >{
    using Base = Parser<D, ParserDerived<D> >;

    using ModuleType = typename Base::ModuleType;
    using DType = typename Base::DType;
};


int main() {
    Parser<double> t;

    ParserDerived<double> d;
}

解决方案

Here's what happens:

  • d gets defined as ParserDerived<double>, so that is instantiated
    • The base class is given as Parser<double, ParserDerived<double>>, so that is instantiated
      • DType gets defined as double
      • DerivedType gets defined as ParserDerived<double>
      • ModuleType gets defined as Module<ParserDerived<double>>
      • ModuleOptions gets defined as Module<ParserDerived<double>>::OptionsType, so Module<ParserDerived<double>> is instantiated
        • DType gets defined as ParserDerived<double>::DTypeERROR HERE
        • OptionsType gets defined as int
    • Base gets defined as Parser<double, ParserDerived<double>>
    • ModuleType gets defined as Parser<double, ParserDerived<double>>::ModuleType
    • DType gets defined as Parser<double, ParserDerived<double>>::DType

If you draw out the instantiations like that, it becomes clear that DType is used before it is defined. It's not immediately obvious that the template instantiation has to be performed sequentially like this, but dyp's comment on your question already answers that it's a valid means of template instantiation, and you can see that it's what multiple compilers do.

You will have to re-work your design. In this particular case, I think a very workable approach would be to mimic the standard library (a bit) and provide a parser traits class. You would move the definitions of ModuleType and DType there, so that accessing those definitions would not require instantiation of the parser class.

In response to your comment:

It shouldn't matter whether you comment the derived class's DType since that cannot seen regardless of whether it's defined, but it's a good question why the base class's DType doesn't get used in its place. Parser<double, ParserDerived<double>> is getting instantiated in order to use it as a base class, but during that instantiation it isn't seen as the base class yet. After the instantiation has been performed, the compiler would first make sure that Parser<double, ParserDerived<double>> is suitable as a base class, and only then would it become the base class.

For a shorter example that more clearly shows this:

template <class B> struct A {
  static void f(A &);
  static decltype(f(*(B*)0)) g();
};
struct B : A<B> { };

Since B derives from A<B>, A<B>::f(A<B> &) should be callable when passed an lvalue of type B. That does not, however, prevent the compiler from complaining about the declaration of g, and clang's error message quite explicitly calls A<B> and B unrelated types:

error: non-const lvalue reference to type 'A<B>' cannot bind to a value of unrelated type 'B'

Here too this happens because B only becomes known as deriving from A<B> after the instantiation of A<B> has completed.

这篇关于如何避免简单的递归模板typedef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆