我有一个有两个模板参数的模板类
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
答案 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
:
使用所谓的普通名称 ::A
而不是注入的名称:
B<T,U,::A> test;
这是可能的,因为§14.6.1/ 5(在C ++ 03中为14.6.1 / 2c):
当使用模板的正常名称(即来自封闭范围的名称,而不是inject-class-name)时,它总是引用类模板本身而不是模板的特化。 [...]
明确使用注入的,但将其指定为模板:
B<T,U,A::template A> test;
这两种方法都被确认为在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;