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

Pix*_*ist 11 c++ templates

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

template <class T, class U> class A  /* ... */
Run Code Online (Sandbox Code Playgroud)

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

template <class T, class U, template<class X, class Y> class Z>
class B
{
    typedef typename Z<T,U>::pointer pointer;
};
Run Code Online (Sandbox Code Playgroud)

是否无法在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;
  }
};
Run Code Online (Sandbox Code Playgroud)

执行相同操作的自由函数根本不存在问题.

template<class T, class U>
B<T, U, A> bar (void)
{
    B<T,U,A> test;
    return test;
}
Run Code Online (Sandbox Code Playgroud)

换句话说:是否有任何规则我没有倒下来阻止我使用我所在类的名称作为模板参数?


代码是:

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;
}
Run Code Online (Sandbox Code Playgroud)

MSVC 2012编译器给出了编译器错误3200.

'A<T,U>' : invalid template argument for template parameter 'Z', expected a class template
Run Code Online (Sandbox Code Playgroud)

jog*_*pan 10

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

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

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

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

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

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

但是,在成员声明中给出了C++ 11规则

B<T,U,A> test;
Run Code Online (Sandbox Code Playgroud)

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

但是,编译器在这种情况下混淆并不罕见.有两种有效的方法可以告诉他们如何解释A:

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

    B<T,U,::A> test;
    
    Run Code Online (Sandbox Code Playgroud)

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

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

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

    B<T,U,A::template A> test;
    
    Run Code Online (Sandbox Code Playgroud)

两种方法都已被确认为在MSVC中解决了这个问题.