如何推断 CRTP 中的类型?
Posted
技术标签:
【中文标题】如何推断 CRTP 中的类型?【英文标题】:How to infer the type in CRTP? 【发布时间】:2016-11-09 13:14:47 【问题描述】:我想在 C++ 模板中实现 CRTP。代码如下:
template <typename T>
class A
public:
typedef typename T::Scalar Scalar;
;
template <typename T>
struct B:public A<B<T> >
public:
typedef T Scalar;
;
但是当我compile the code 编译器显示:
error: no type named ‘Scalar’ in ‘struct B<int>’
谁能解释一下?
【问题讨论】:
Duplicate,但是这个问题要清楚得多,所以我认为一旦有答案,我们应该反向欺骗关闭另一个问题。 @Quentin 对此有任何想法吗? My answer to that linked question也适合这个问题(当您实例化A<B>
时B
不完整),但我觉得在这里复制/修补/粘贴我的答案会很便宜。
@Quentin 那么,我必须添加一个特征?
【参考方案1】:
问题在于A<T>
的API 说必须定义T::Scalar
,而template<typename U> struct B:public A<B<U> >
中还没有这种情况。
简单的改变就是修复A的API:
template <typename SCALAR>
class A
public:
typedef SCALAR Scalar;
;
template <typename T>
struct B:public A<T>
;
在抽象术语中,A<T>, T::Scalar
的使用是传递名称的一种形式,而 A<SCALAR>
是常规传递参数。当名称尚未在您需要的地方定义时,按名称传递是有问题的。
[编辑] 而且因为它看起来不明显,你仍然可以保留 CRTP:
template <typename SCALAR, typename CRTP>
class A
public:
typedef SCALAR Scalar;
;
template <typename T>
struct B:public A<T, B<T>>
;
【讨论】:
但是这里A
根本没有B
的任何信息。 A<T>
现在怎么能使用B<T>
的任何方法? CRTP 不见了!
@iammilind:嗯,你可以保持 CRTP 和 still 分别通过 SCALAR
。两者并不相互排斥。 B::Scalar
不需要 CRTP。
您可能假设,在这种情况下,CRTP 仅用于获取Scalar
;正确的?但是如果 CRTP 和 Scalar
两者都需要用于不同的目的呢?从 Q 看来,OP 似乎想要 CRTP,而另一方面,他/她偶然发现了 Scalar
的内部 typedef
。
@iammilind: T::Scalar
不是问题中的内部 typedef,它是 A<T>
的传递名称参数。【参考方案2】:
错误是由于类型不完整。看下面一行:
template<typename T>
struct B : public A<B<T>>
B<T>
的主体尚未启动,您正在使用它作为构建A<T>
的参数。实际上这是允许的,但正如Quentin's answer 中提到的,这是 CRTP 的一个警告。除了这个答案,这里还有另一种方式:
template <template<typename> class Base, typename T>
class A // use `<typename...>` for C++11
public:
typedef T Scalar;
// use `Base<T>` wherever required
;
template <typename T>
struct B : public A<B, T>
public: // ^^^^^^^
typedef T Scalar;
;
【讨论】:
【参考方案3】:换个角度看,
B<int> b is called which will invoke
B<int> : A<B<int> > which will further invoke // B is not yet done waiting on A<B<int> >
A<B<int>> typedef B<int>::Scalar Scalar will try to fetch B<int> //which is not yet constructed as many pointed out.
以下代码不是解决方案,但会解释问题所在。下面的代码编译,因为我们打破了循环。
template <typename T>
class A
public:
typedef typename T::Scalar Scalar;
;
template <typename T>
class B:public A<B<T> >
public:
typedef T Scalar;
;
template <>
class A<B<int> >
public :
typedef int Scalar;
;
int main()
B<int> b;
现在的解决方案是打破循环或避免循环。我们可以相应地想出几个解决方案。
【讨论】:
以上是关于如何推断 CRTP 中的类型?的主要内容,如果未能解决你的问题,请参考以下文章