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的代码应该很好,它被Clang 3.2接受,但不是由GCC 4.7接受(4.4和4.6也失败,基本相同的错误,但是4.4产生一个稍微不同的消息).这是我的shell的输出:
$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] $
问题:这是GCC中的错误还是我错过了什么?
编辑:我有点困惑:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55576的GCC错误报告在评论#9中表示,评论3中的代码是“有效的”.这是什么意思?看起来像海湾合作委员会的人认为它实际上是一个错误,否则他们已经关闭了? OTOH从@Potatoswatter的答案似乎很清楚,它应该是正确的,我应该向Clang提交一个错误报告(或者是否已经有这样的错误报告?)
请注意,我犹豫,将答案标记为接受,直到上述澄清.既然这两个答案都是有帮助的(一个解释一个解决方案),我给了一个upvote.
奖金问题:由于我没有非叙述性的代码,我想知道别人的感受.我试图创建一个SCCEE,消除所有的分心,并集中在技术问题上.这就是我更喜欢考虑这些事情.那是错的吗
另外@EdHeal:为什么代码容易发生灾难? (你不认为这是真实世界的代码,对吗?)
编辑2:谢谢大卫,刚刚注意到你的编辑.我会将您的答案标示为现在接受,我也看到您对GCC错误报告发表了评论.我认为这个问题的要点得到了回答,海湾合作委员会再次提醒.感谢大家.
解决方法
In a class member access expression (5.2.5),if the
.
or->
token is immediately followed by an identifier followed by a<
,the identifier must be looked up to determine whether the<
is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found,it is then looked up in the context of the entire postfix-expression and shall name a class or function template. If the lookup in the class of the object expression finds a template,the name is also looked up in the context of the entire postfix-expression and— if the name is not found,the name found in the class of the object expression is used,otherwise
— if the name is found in the context of the entire postfix-expression and does not name a class template,otherwise
— if the name found is a class template,it must refer to the same entity as the one found in the class of the object expression,otherwise the program is ill-formed.
请注意,此过程是无用的,因为已经需要使用template关键字来消除<令牌,因为子表达式u [0]的类型取决于模板参数. 以这种方式执行此操作的原因是为了简化解析,只要在嵌套名称限定符中使用template-id,例如u [0] .as< T> :: bar.baz其中bar是基类的typedef.
C 11删除了三个项目符号,简化了过程
The identifier is first looked up in the class of the object expression. If the identifier is not found,it is then looked up in the context of the entire postfix-expression and shall name a class template.
所以这是一个bug,而不是像以前说过的一个bug.名称查找角箱需要删除.
此外,看起来这个怪癖可以被利用来允许单个模板表达式交替地引用一个类或一个函数.不知道这是否有用,但在C11中却是新的.