Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify bug in GCC

I'd like to verify that the following is a bug in GCC and not in my understanding of C++. Consider the following code:

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 the code should be fine, it is accepted by Clang 3.2, but not by GCC 4.7 (4.4 and 4.6 also fail with basically the same error but 4.4 produces a slightly different message). Here's the output from my 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]
$ 

Question: Is this a bug in GCC or am I missing something?

EDIT: I'm a bit confused: The GCC bug report at http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55576 says at comment #9 that the code in comment #3 is "valid". What exactly does this mean? It looks like the GCC folks think that it actually is a bug, otherwise they would have closed it already? OTOH the answer from @Potatoswatter seems to be quite clear that it should be correct and I should file a bug report against Clang (or is there already such a bug report?)

Note that I hesitate to mark an answer as accepted until the above is clarified. Since both answers are already helpful (one to explain, one to work-around), I gave both an upvote.

Bonus question: Since I got a downvote for non-narrative code, I wonder how others feel. I tried to create a SCCEE which removes all distractions and concentrates on the technical issue. That's how I prefer thinking about these things. Is that wrong?

Also, @EdHeal: Why is the code prone to disaster? (You don't think that is the real-world code I have, right?)

EDIT2: Thanks, David, just noticed your edit. I'll mark your answer as accepted now and I also saw that you commented on the GCC bug report. I think the main point of this question is thereby answered and GCC got another reminder. Thanks everyone.

like image 358
Daniel Frey Avatar asked Feb 25 '13 10:02

Daniel Frey


1 Answers

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.

like image 197
Potatoswatter Avatar answered Oct 21 '22 07:10

Potatoswatter