g ++/Clang中的另一个错误?[C++模板很有趣]

Pra*_*rav 31 c++ templates

查看以下代码(仅为了好玩而编写)

namespace N
{
   template<typename T>
   struct K
   {

   };
}
template<typename T>
struct X
{
   typename T::template K<T> *p; //should give error 
                                 //N::K<int> has no template member named `K`
};

int main()
{
   X<N::K<int> > l;
}
Run Code Online (Sandbox Code Playgroud)

代码在g ++(4.5.1)和Clang上编译,而Comeau和Intel C++给出(类似)错误.

我在Comeau上遇到的错误是:

"ComeauTest.c", line 13: error: class "N::K<int>" has no member "K"
     typename T::template K<T> *p;
                          ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18

"ComeauTest.c", line 13: error: expected an identifier
     typename T::template K<T> *p;
                           ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18
Run Code Online (Sandbox Code Playgroud)

所以我的问题是"代码示例是否格式错误?" 据我说"是".这是否意味着这是g ++/Clang中的另一个错误?

Joh*_*itb 44

为什么GCC和Clang认为他们是对的

K,这是注入的类名,具有双重性质K<int>.您可以使用它而不使用模板参数.然后它指K<int>(对于它自己的类型).

它后面也可以是模板参数列表.IMO有理由说你需要加上前缀,template因为解析器的含糊不清<.然后它引用由模板参数确定的指定类型.

因此,它可以被视为成员模板和嵌套类型,具体取决于它是否后跟模板参数列表.当然,K并不是真正的会员模板.不过,注入类名的双重性质在我看来更像是一个黑客.

标准有一个例子,如下所示:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
   typename Derived::Base b; // error: ambiguous
   typename Derived::Base<double> d; // OK
};
Run Code Online (Sandbox Code Playgroud)

人们可能倾向于从中得出结论,意图是你可以放弃template.标准说

对于要由模板参数显式限定的模板名称,必须知道名称才能引用模板.

我看不出这是不适用的T::K<T>.如果T是依赖类型,那么你可以向后倾斜,因为K在解析它时你不知道是什么意思,所以为了对代码有所了解,你只需要能够为它添加前缀template.请注意,n3225也有这个例子,但它不是缺陷:template如果你在C++ 0x中查找模板自己的范围(它被称为"当前实例化"),你可以正式离开.

所以到目前为止,Clang和GCC都很好.


为什么Comeau是对的

只是为了使它更复杂,我们将不得不考虑的构造函数K<int>.隐式声明了默认构造函数和复制构造函数.除非使用的名称查找将忽略函数(构造函数)名称K<int>::K,K<int> 否则名称将引用构造函数.会typename T::K忽略函数名吗?3.4.4/3说明了详细的类型说明符,它typename ...是以下之一:

如果名字是一个合格的-ID,名称是根据其资历抬头,如3.4.3所描述的,但是忽略已宣告任何非类型名称.

但是,a typename ...使用不同的查找.14.6/4说

通常的限定名称查找(3.4.3)用于查找qualified-id,即使存在typename也是如此.

3.4.3的通常限定查找不会忽略非类型名称,如附加到14.6/4的示例所示.因此,我们将找到3.4.3.1/1a中指定的构造函数(仅当非类型未被忽略时才会发生的附加扭曲由后来的缺陷报告添加,所有流行的C++ 03编译器都会实现虽然):

如果nested-name-specifier指定了一个类C,并且在嵌套-name-specifier之后指定的名称(在C中查找时)是C的注入类名(第9节),则该名称被认为是名称,例如,应仅在出现的类定义之外的构造定义的声明符-ID中使用的构造方法名的类C的构造.

所以最后,我认为comeau是正确的诊断,因为你试图将模板参数列表放到非模板上,并且还违反了上一部分引用的要求(你在其他地方使用名称).

让我们通过访问注入名称更改派生类,所以没有构造函数的名字翻译时,你真的访问类型,这样就真的可以追加模板参数:

// just replace struct X with this:
template<typename T>
struct X
{
   struct Derived : T { };
   typename Derived::template K<T> *p;
};
Run Code Online (Sandbox Code Playgroud)

现在所有的东西都用来了!注意我已经做了问题报告来铿锵有关.请参见不正确的构造函数名称解析.顺便说一句,如果你声明一个默认的构造函数K,你可以看到如果使用的话,会给出更好的错误信息T::K<int>

"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
          not a template
     typename T::template K<T> *p;
Run Code Online (Sandbox Code Playgroud)

  • 真棒解释!没有更多可以说! (8认同)