如何避免简单的递归模板typedef [英] How to avoid simple recursive template typedefs
问题描述
我有以下简单的问题:
一个类 template< typename D> Parser< Parser>
定义 ModuleType
为的Parser
。我想将解析器类型注入到模块中,以便能够从解析器中再次提取几种类型。这很方便,因为模块中只需要一个模板参数。但是如果解析器需要在模块中定义一些类型,例如 OptionsType
,在 Parser
通过使用声明使用ModuleOptions = ...
显然不适用于派生类 ParserDerived
的倾向。错误:错误:没有在'struct ParserDerived< double>'中使用DType = typename Parser :: DType;
$ b
我害怕使用这种模式,因为我可能会在未来意识到,所有我的这些模式的构造崩溃成吨的难以理解的编译器失败...
对于下面的问题,什么是更好的方法?
#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?
#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 asParserDerived<double>
, so that is instantiated- The base class is given as
Parser<double, ParserDerived<double>>
, so that is instantiatedDType
gets defined asdouble
DerivedType
gets defined asParserDerived<double>
ModuleType
gets defined asModule<ParserDerived<double>>
ModuleOptions
gets defined asModule<ParserDerived<double>>::OptionsType
, soModule<ParserDerived<double>>
is instantiatedDType
gets defined asParserDerived<double>::DType
← ERROR HEREOptionsType
gets defined asint
Base
gets defined asParser<double, ParserDerived<double>>
ModuleType
gets defined asParser<double, ParserDerived<double>>::ModuleType
DType
gets defined asParser<double, ParserDerived<double>>::DType
- The base class is given as
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屋!