如何推断 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&lt;B&gt;B不完整),但我觉得在这里复制/修补/粘贴我的答案会很便宜。 @Quentin 那么,我必须添加一个特征? 【参考方案1】:

问题在于A&lt;T&gt; 的API 说必须定义T::Scalar,而template&lt;typename U&gt; struct B:public A&lt;B&lt;U&gt; &gt; 中还没有这种情况。

简单的改变就是修复A的API:

template <typename SCALAR>
class A
public:
    typedef SCALAR Scalar;
;
template <typename T>
struct B:public A<T> 
;

在抽象术语中,A&lt;T&gt;, T::Scalar 的使用是传递名称的一种形式,而 A&lt;SCALAR&gt; 是常规传递参数。当名称尚未在您需要的地方定义时,按名称传递是有问题的。

[编辑] 而且因为它看起来不明显,你仍然可以保留 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&lt;T&gt;现在怎么能使用B&lt;T&gt;的任何方法? CRTP 不见了! @iammilind:嗯,你可以保持 CRTP 和 still 分别通过 SCALAR。两者并不相互排斥。 B::Scalar 不需要 CRTP。 您可能假设,在这种情况下,CRTP 仅用于获取Scalar;正确的?但是如果 CRTP 和 Scalar 两者都需要用于不同的目的呢?从 Q 看来,OP 似乎想要 CRTP,而另一方面,他/她偶然发现了 Scalar 的内部 typedef @iammilind: T::Scalar 不是问题中的内部 typedef,它是 A&lt;T&gt; 的传递名称参数。【参考方案2】:

错误是由于类型不完整。看下面一行:

template<typename T>
struct B : public A<B<T>> 

B&lt;T&gt; 的主体尚未启动,您正在使用它作为构建A&lt;T&gt; 的参数。实际上这是允许的,但正如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 中的类型?的主要内容,如果未能解决你的问题,请参考以下文章

禁用 CRTP 模板中的复制分配

CRTP:根据派生类内容启用基类中的方法

CRTP 和多级继承

C++ 隐藏继承层次结构中的成员函数,紧盯 CRTP

CRTP——访问不完整的类型成员

使用带有附加类型参数的奇怪重复模板模式 (CRTP)