11

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

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;
};

是否不可能在 Z 为 A 的 A 中创建 B 的实例?

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
4

2 回答 2

10

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

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

在类模板的定义中,可以使用A该名称,因为它被“注入”到. 因此,众所周知,您可以使用as well as以及 as well as等来指代. 根据上面引用的规则,所有这些表达式都引用当前实例化(即特定类型,如),而不是模板本身的名称。AAAA::AA::A::AAA<int,float>A

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

像普通(非模板)类一样,类模板有一个注入类名(第 9 条)。注入的类名可以用作模板名或类型名。当它 [...] 用作模板模板参数的模板参数 [...] 时,它指的是类模板本身。否则,它相当于模板名称后跟 <> 中包含的类模板的模板参数。

(请注意,在 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):

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

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

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

这两种方法都被证实可以在 MSVC 中解决这个问题。

于 2013-06-25T02:29:51.257 回答
0

如果 A 类在 B 类之前定义,我会收到错误消息:“B”没有命名类型(换句话说,B 尚未定义)。这是你得到的错误吗?它可以通过将 B 放在 A 前面来修复,或者,如果两者相互引用,则可以通过在 A 之前向前声明 B 类,如下所示:

template <typename T, class U, template<typename X, class Y> class Z> class B;
于 2013-06-25T01:45:48.130 回答