2013-02-25 68 views
10

我想验证以下是GCC中的错误,而不是我对C++的理解。考虑下面的代码:验证GCC中的错误

struct A 
{ 
    struct B 
    { 
     template< typename U > U as() const { return U(); } 
    }; 

    B operator[](int) const { return B(); } 
}; 

template< typename T > 
struct as 
{ 
    template< typename U > 
    static T call(const U& u) 
    { 
     return u[ 0 ].as<T>(); // accepted by Clang 3.2, rejected by GCC 4.7 
     // return u[ 0 ].template as<T>(); // does not help and is IMHO not needed 
     // return u[ 0 ].A::B::as<T>(); // accepted by GCC 4.7 
    } 
}; 

int main() 
{ 
    as<int>::call(A()); 
} 

IMHO代码要细,它是由铛3.2接受,但不是由GCC 4.7(4.4和4.6也失败,基本上是相同的错误,但4.4产生稍微不同的消息) 。下面是从我的壳输出:

$ clang++-3.2 -O3 -Wall -Wextra -std=c++0x t.cc -o t 
$ g++-4.7 -O3 -Wall -Wextra -std=c++0x t.cc -o t 
t.cc: In static member function ‘static T as<T>::call(const U&)’: 
t.cc:17:21: error: invalid use of ‘struct as<T>’ 
t.cc: In static member function ‘static T as<T>::call(const U&) [with U = A; T = int]’: 
t.cc:18:4: warning: control reaches end of non-void function [-Wreturn-type] 
$ 

问:这是海湾合作委员会中的一个bug还是我失去了一些东西?

编辑:我有点困惑:在http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55576的GCC错误报告在评论#9中说,评论#3中的代码是“有效的”。这到底是什么意思?它看起来像海湾合作委员会的人认为它实际上一个错误,否则他们会已经关闭它? OTOH @Potatoswatter的答案似乎很清楚,它应该是正确的,我应该提交一份针对叮铛的错误报告(或者是否已经有这样的错误报告?)

请注意,我毫不犹豫地将答案标记为接受直到上述内容被澄清。既然这两个答案都已经有帮助(一个解释,一个解决方法),我同时给出了两个答案。

奖金问题:由于我得到了非叙述性代码的低估,我想知道别人的感受。我试图创建一个SCCEE,它消除了所有的干扰并专注于技术问题。这就是我更喜欢考虑这些事情的方式。那是错的吗?

另外,@EdHeal:为什么代码容易发生灾难? (你不认为这是我真实世界的代码,对吗?)

EDIT2:谢谢大卫,刚刚注意到你的编辑。我会将您的答案标记为已接受,并且我还看到您对GCC错误报告发表了评论。我认为这个问题的主要意见从而得到了答复,海湾合作委员会得到了另一个提醒。感谢大家。

+0

我相信这是一个已知的错误。 – Potatoswatter 2013-02-25 10:20:48

+4

但是,由于'u [0]'是一个依赖表达式,所以'template'关键字是必需的。解析器需要能够在不知道'U'的情况下找出'<'符号的含义,并且不需要查找'>'。 – Potatoswatter 2013-02-25 10:23:23

+0

这就是我所想的,Clang应该拒绝这些代码。 – Xeo 2013-02-25 10:25:13

回答

5

这是对一个棘手的角落语言。 GCC被施加从C++ 03§3.4.5/ 1的规则:

在一类成员访问表达式(5.2.5)中,如果.->令牌之后紧接着一个标识符后跟一个<,必须查找标识符以确定<是模板参数列表(14.2)的开始还是小于运算符。标识符首先在对象表达式的类中查找。如果找不到标识符,则在整个后缀表达式的上下文中查找它,并命名类或函数模板。如果对象表达式的类中的查找查找到模板,则在整个后缀表达式的上下文中查找该名称,并且如果找不到该名称,则在整个后缀表达式的类中找到该名称对象表达被使用,否则

- 如果名称在整个后缀表达式的上下文中发现,没有指定类模板,在该对象的类别的表达的发现名称被使用,否则

- 如果找到的名称是类模板,则它必须引用与在对象表达式的类中找到的实体相同的实体,否则该程序是格式不正确的。

注意,因为template关键字已经被要求澄清对<令牌,因为子表达式u[0]的类型依赖于一个模板参数这一过程是无用的。

这样做的原因是在template-id用于嵌套名称限定符的情况下简化解析,例如u[ 0 ].as<T>::bar.baz其中bar是对基类的typedef。

C++ 11删除三个要点简化了工艺,以

标识符首先在该对象的类别的表达抬头。如果找不到标识符,则在整个后缀表达式的上下文中查找它,并命名一个类模板。

所以这是一个错误,而不是我之前说过的那个旧的错误。名称查找角落案件需要被删除。

此外,它看起来像这个怪癖可以被利用来允许单个模板表达式交替引用类或函数。不确定这是否有用,但它在C++ 11中是新的。

+0

等一下,所以全局'as'的名字实际上是为嵌套表达式找到的?笏。 – Xeo 2013-02-25 11:50:49

+0

@Xeo Yep,只是因为他们想在最后一段作品中使我的设计变得像我的设计一样。 – Potatoswatter 2013-02-25 11:52:06

+0

这是来自公布的标准吗?我在n3337或n3485中看不到这些子弹。 – ecatmur 2013-02-25 11:53:41

1

问题在于as作为类和模板名都被注入template<...> struct as的范围;这就是为什么gcc抱怨“invalid use of ‘struct as<T>’”。

我不太清楚GCC是否正确(它的下跌,为会员表情名字查找规则),但解决方法是使用decltype

return u[ 0 ].decltype(u[ 0 ])::template as<T>(); 
+0

注入名称不可能在那里有效,因为它是一个无关的嵌套名称类。 – Xeo 2013-02-25 11:43:29

+0

@Xeo我不确定你的意思是“有效”。编译器不知道'U'是不相关的,它也不知道它具有'as'成员模板。 – ecatmur 2013-02-25 11:57:05