10

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

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

恕我直言,代码应该没问题,它被 Clang 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]
$ 

问题:这是 GCC 中的错误还是我遗漏了什么?

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

请注意,在澄清上述内容之前,我会犹豫将答案标记为已接受。由于这两个答案都已经很有帮助(一个解释,一个解决),我给了两个答案。

奖励问题:由于我对非叙述性代码投了反对票,我想知道其他人的感受。我试图创建一个 SCCEE,它可以消除所有干扰并专注于技术问题。这就是我更喜欢思考这些事情的方式。那是错的吗?

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

EDIT2:谢谢,大卫,刚刚注意到你的编辑。我现在会将您的答案标记为已接受,并且我还看到您对 GCC 错误报告发表了评论。我认为这个问题的要点由此得到解答,GCC 得到了另一个提醒。谢谢大家。

4

2 回答 2

5

This is a tricky corner of the language. GCC is applying the rule from C++03 §3.4.5/1:

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, the name found in the class of the object expression is used, 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.

Note that this process was useless because the template keyword is already required to disambiguate the < token, since the type of the subexpression u[0] depends on a template argument.

The reason for doing it this way is to simplify parsing in the case that the template-id is used in a nested-name-qualifier, for example u[ 0 ].as< T >::bar.baz where bar is a typedef to a base class.

C++11 removes the three bullet points simplifies the process to

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.

So this is a bug, and not an old one as I had said previously. The name lookup corner case needs to be removed.

Also, it looks like this quirk could be exploited to allow a single templated expression to alternately refer to a class or a function. Not sure if that's useful, but it's new in C++11.

于 2013-02-25T11:47:59.587 回答
1
于 2013-02-25T11:22:04.697 回答