g ++ / Clang中的另一个错误? [C ++模板很有趣] [英] Another bug in g++/Clang? [C++ Templates are fun]

查看:145
本文介绍了g ++ / Clang中的另一个错误? [C ++模板很有趣]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

查看以下代码(仅为了有趣而写)

Check out the following code (written just for fun)

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

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

The code gets compiled on g++(4.5.1) and Clang whereas Comeau and Intel C++ give (similar) errors.

我在Comeau上得到的错误是:

The errors that I get on Comeau are :

"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

所以我的问题是代码示例是否错误?根据我是是。

So my question is "Is the code sample ill-formed ?" According to me "Yes". Does that mean this is yet another bug in g++/Clang?

推荐答案

为什么GCC和Clang认为它们是正确的



这是注入的类名,在 K 。您可以使用它没有模板参数。然后它引用 K (到它自己的类型)。

Why GCC and Clang think they are right

K, which is the injected class name, has a dual nature in the scope of K<int>. You can use it without template arguments. Then it refers to K<int> (to its own type).

它后面也可以是模板参数列表。 IMO可以合理地说,您需要在它前面加上模板,因为解析器模糊性与< 。然后它引用由模板参数确定的指定类型。

It can be followed by a template argument list too. IMO it's reasonable to say that you need to prefix it with template because of the parser ambiguity with the < that follows. It then refers to the specified type that's determined by the template arguments.

因此,它可以被视为成员模板和嵌套类型,具体取决于它是否后跟模板参数列表。当然, K 不是真正的成员模板。注入类名的双重性质似乎对我来说更多的黑客反正,虽然。

So it can be treated as a member template and as a nested type, depending on whether it's followed by a template argument list. Of course, K is not really a member template. The dual nature of the injected class name seems to me more of a hack anyway, though.

标准版的示例如下:

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

一个人可能倾向于从这里得出结论,意图是你可以离开 template 。标准表示

One might be inclined to conclude from this that the intent is that you could leave off the template. The Standard says


要通过模板参数显式限定模板名称,必须知道名称才能引用模板。

For a template-name to be explicitly qualified by the template arguments, the name must be known to refer to a template.

我看不到这不适用于 T :: K< T& code>。如果 T 是一个依赖类型,那么你可以回退,因为你不知道 K 它,所以要使任何意义的代码,你只需要能够前缀与模板。注意,n3225也有这个例子,但它不是一个缺陷:你可以正式离开模板如果你查找到模板自己的范围在C ++ 0x(它被称为当前实例化)。

I can't see how this wouldn't apply to T::K<T>. If T is a dependent type then you can just lean back because you can't know what K refers to when parsing it, so to make any sense of the code, you just have to be able to prefix it with template. Notice that n3225 has that example too, but it's not a defect there: You can officially leave off template if you lookup into the template's own scope in C++0x (it's called the "current instantiation").

到目前为止,Clang和GCC都没问题。

So until now, Clang and GCC are fine.

为了使它更复杂,我们必须考虑 K< ; int> 。有一个默认构造函数和一个隐式声明的拷贝构造函数。名称 K 将引用 K 的构造函数>除非使用的名称查找将忽略函数(构造函数)名称。 typename T :: K 忽略函数名? 3.4.4 / 3说明详细的类型说明符, typename ... 是以下之一:

Just to make it even more complicated, we will have to consider the constructors of K<int>. There is a default constructor and a copy constructor implicitly declared. A name K<int>::K will refer to the constructor(s) of K<int> unless the name lookup used will ignore function (constructor) names. Will typename T::K ignore function names? 3.4.4/3 says about elaborated type specifiers, which typename ... is one of:


如果名称是qualified-id,则根据3.4.3中所述的资格来查找名称,但忽略已声明的任何非类型名称。

If the name is a qualified-id, the name is looked up according its qualifications, as described in 3.4.3, but ignoring any non-type names that have been declared.

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

However, a typename ... uses different lookup. 14.6/4 says


通常的限定名称查找(3.4.3)用于查找qualified-id, 。

The usual qualified name lookup (3.4.3) is used to find the qualified-id even in the presence of typename.

通常的3.4.3的合格查询不会忽略非类型名称,如附件14.6 / 4。因此,我们将找到由3.4.3.1/1a指定的构造函数(只有当非类型不是被忽略时才会发生的额外的扭曲由后来的缺陷报告添加,所有的流行的C ++ 03编译器实现):

The usual qualified lookup of 3.4.3 won't ignore non-type names, as illustrated by the example attached to 14.6/4. So, we will find the constructor(s) as specified by 3.4.3.1/1a (the additional twist that this only happens when non-types are not ignored was added by a later defect report, which all popular C++03 compilers implement though):


如果嵌套名称说明符提名一个类C,当在C中查找时,嵌套名称说明符是C的注入类名(第9节),该名称被认为是命名类C的构造函数。这样的构造函数名称只能在声明符中使用-id出现在类定义之外的构造函数定义。

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition that appears outside of the class definition.

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

So in the end, I think comeau is correct to diagnose this, because you try to put a template argument list onto a non-template and also violate the requirement quoted in the last part (you use the name elsewhere).

让我们通过派生类访问注入的名称来更改它,因此不会发生构造函数名称转换, em>访问类型,以便真正可以附加模板参数:

Let's change it by accessing the injected name by a derived class, so no constructor name translation occurs, and you really access the type so that you really can append the template arguments:

// just replace struct X with this:
template<typename T>
struct X
{
   struct Derived : T { };
   typename Derived::template K<T> *p;
};

注意我已经做了问题报告ang这个确切的事情。请参见构造函数名称解析不正确。 BTW,如果你在 K 中声明一个默认的构造函数,你可以看到如果你使用 T :: K< int> / code>

Everything compiles now with comeau too! Notice I already did problem report to clang about this exact thing. See Incorrect constructor name resolution. BTW, if you declare a default constructor in K, you can see comeau give a better error message if you use 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;

这篇关于g ++ / Clang中的另一个错误? [C ++模板很有趣]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆