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 tovoid Type<A::B>()
main.cpp:21: undefined reference tovoid 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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With