验证GCC中的错误

时间:2013-02-25 10:11:46

标签: c++ templates gcc c++11

我想验证以下是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会产生略有不同的消息)。这是我的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中的代码是“有效的”。这到底是什么意思?看起来像GCC的人认为它实际上一个bug,否则他们已经关闭了它? OTOH来自@Potatoswatter的回答似乎很清楚它应该是正确的我应该提交针对Clang的错误报告(或者是否已经有这样的错误报告?)

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

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

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

EDIT2:谢谢,大卫,刚注意到你的编辑。我现在已经接受了你的回答,我也看到你对GCC错误报告发表了评论。我认为这个问题的要点得到了回答,海湾合作委员会再次提醒。谢谢大家。

2 个答案:

答案 0 :(得分:5)

这是该语言的一个棘手的角落。 GCC正在应用C ++03§3.4.5/ 1中的规则:

  

在类成员访问表达式(5.2.5)中,如果.->标记后面紧跟着标识符后跟<,则必须查找标识符确定<是模板参数列表(14.2)的开头还是小于运算符。首先在对象表达式的类中查找标识符。如果未找到标识符,则在整个postfix-expression的上下文中查找它,并命名一个类或函数模板。如果对象表达式的类中的查找找到模板,则还会在整个后缀表达式的上下文中查找该名称,并且

     

- 如果找不到名称,则使用在对象表达式的类中找到的名称,否则使用

     

- 如果在整个postfix-expression的上下文中找到该名称并且未命名类模板,则使用在对象表达式的类中找到的名称,否则

     

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

请注意,此过程无用,因为template关键字已被用于消除<令牌的歧义,因为子表达式u[0]的类型取决于模板参数。

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

C ++ 11删除了三个要点,简化了流程

  

首先在对象表达式的类中查找标识符。如果找不到标识符,则在整个postfix-expression的上下文中查找它,并命名一个类模板。

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

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

答案 1 :(得分:1)

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

我不太确定gcc是否正确(这取决于成员表达式上的名称查找规则),但解决方法是使用decltype

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