如何在类本身中使用类名作为模板参数?

时间:2013-06-25 01:34:03

标签: c++ templates

我有一个有两个模板参数的模板类

template <class T, class U> class A  /* ... */

和另一个模板类,它接受一个带有两个参数作为模板参数的模板类。

template <class T, class U, template<class X, class Y> class Z>
class B
{
    typedef typename Z<T,U>::pointer pointer;
};

是否无法在A中创建B的实例,其中Z是A?

template <class T, class U>
class A  
{
public:
  B<T,U,A> foo (void) // compiler complaining here
  {
    B<T,U,A> test; // and here
    return test;
  }
};

执行相同操作的自由功能根本不存在问题。

template<class T, class U>
B<T, U, A> bar (void)
{
    B<T,U,A> test;
    return test;
}

换句话说:是否有任何规则我没有失败,这使我无法使用我所在类的名称作为模板参数?


代码是:

template <class T, class U, template<class X, class Y> class Z>
class B
{
  typedef typename Z<T,U>::pointer pointer;
};

template <class T, class U>
class A 
{
public:
  B<T,U, A> foo (void) 
  {
    B<T,U,A> test;
    return test;
  }
};

template<class T, class U>
B<T, U, A> bar (void)
{
    B<T,U,A> test;
    return test;
}

int main (void)
{
 return 0;
}

MSVC 2012编译器提供Compiler Error 3200

'A<T,U>' : invalid template argument for template parameter 'Z', expected a class template

2 个答案:

答案 0 :(得分:10)

您的编译器MSVC似乎遵循§14.6.2.1/ 1 C ++ 11中规定的一般规则:

  

名称是指当前实例化,如果它是,[...]在类模板的定义中,[...]注入类名称[...]类模板[...]

在类模板A的定义中,可以使用名称A,因为它被“注入”A的本地(类)范围。因此,着名的是,您可以使用A以及A::A以及A::A::A等来引用A。根据上面引用的规则,所有这些表达式都引用当前实例化(即特定类型,如A<int,float>),而不是模板A本身的名称。< / p>

但是,在§14.6.1/ 1中还有另一条规则:

  

与普通(非模板)类一样,类模板具有注入类名(第9节)。 inject-class-name可以用作模板名称或类型名称。当它被用作模板模板参数[...]的模板参数时,它引用了类模板本身。否则,它等同于模板名称,后跟包含在&lt;&gt;中的类模板的模板参数。

(注意,在C ++ 03中,模板模板参数没有这样的例外;实际上,14.6.1 / 1的措辞完全不同。在C ++ 03下,MSVC对规则的解释可能是正确的。)

但是,在成员声明

中给出了C ++ 11规则
B<T,U,A> test;

A的定义中,名称A显然用作模板模板参数的参数,因此必须解释为模板名称,而不是引用当前实例化的类型名称。

但是,编译器在这种情况下感到困惑的情况并不少见。有两种有效的方法可以告诉他们如何解释A

  1. 使用所谓的普通名称 ::A而不是注入的名称:

    B<T,U,::A> test;
    

    这是可能的,因为§14.6.1/ 5(在C ++ 03中为14.6.1 / 2c):

      

    当使用模板的正常名称(即来自封闭范围的名称,而不是inject-class-name)时,它总是引用类模板本身而不是模板的特化。 [...]

  2. 明确使用注入的,但将其指定为模板:

    B<T,U,A::template A> test;
    
  3. 这两种方法都被确认为在MSVC中解决了这个问题。

答案 1 :(得分:0)

如果在B级之前定义了A级,我就会收到错误:&#39; B&#39;没有命名类型(换句话说,B尚未定义)。这是你得到的错误吗?它可以通过将B放在A前面来固定,或者,如果两者相互引用,可以通过在A之前向前声明B类来修复:

template <typename T, class U, template<typename X, class Y> class Z> class B;