Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this absurd code that compiles fine a bug in both Clang and GCC? [duplicate]

I was playing around with templates today to see if I could get the compiler to deduce the type of an outer class from one of its inner classes. I didn't find my solution (which I suspect is impossible), but while trying to fix an error I ran into very strange behavior that I reduced to the following snippet.

struct A
{
    struct B{};

    template <typename T>
    struct EverythingIsFine
    {
        using Outer = T;
        using Inner = typename T::B::B::B::B::B::B;
    };

    using ItWillBeOkay = EverythingIsFine<B>; // Probably not ok
    using InnerProblem = ItWillBeOkay::Inner; // Still not ok
    using OuterProblem = decltype(B().ItWillBeOkay::Outer::B::B::B
                                     ::B::B::B::~B()); // Not even CLOSE to ok
};

It surprisingly compiles with no warnings and no errors with both Clang and GCC.
The versions of my compilers are gcc version 5.3.1 20160121 (Debian 5.3.1-7) and Debian clang version 3.6.2-3 (tags/RELEASE_362/final) (based on LLVM 3.6.2) and the flag used to compile are -std=c++11 -Wall -Wextra.

I observed that it also compiles fine on Ideone with the C++14 setting.


I then used this simple test to get the exact types of InnerProblem and OuterProblem:

template <class T> void Type();
int main()
{
    Type<A::InnerProblem>();
    Type<A::OuterProblem>();
}

And both compilers report the same types when compiling the test:

In function main:
main.cpp:20: undefined reference to void Type<A::B>()
main.cpp:21: undefined reference to void Type<void>()

That is to say, the type of InnerProblem is A::B and the type of OuterProblem is void.


Is this somehow permitted by the standard or is it a bug in both compilers?
And since I seem to be as confused as my compiler, what is actually happening with this code?

EDIT: As a simplified followup, because I don't understand why two compilers can not give the same result, the following code compiles with Clang, but not with GCC.

struct A
{
    struct B{};

    template <typename T>
    struct EverythingIsFine
    {
        using Inner = typename T::B::B::B;
    };

    using Problem = EverythingIsFine<B>::Inner::B::B::B; // Not ok
};

GCC now outputs the following error:

main.cpp:11:26: error: 'A::B::B' names the constructor, not the type using InnerProblem = EverythingIsFine::Inner::B::B::B; // Not ok

like image 512
tux3 Avatar asked Feb 02 '16 19:02

tux3


1 Answers

It's valid.

The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name." (9/2).

So B::B names the class B, as does B::B::B, and so on.

EDIT:

So typename B::B names the class B, as does typename B::B::B, and so on.

like image 168
Pete Becker Avatar answered Oct 18 '22 21:10

Pete Becker