8

I have the following code

#include <iostream>
#include <string>
using namespace std;

template<class T> struct Tpl;
template<>        struct Tpl<int>    { void print() { cout << "int"    << endl; } };
template<>        struct Tpl<string> { void print() { cout << "string" << endl; } };

int main() {
  typename Tpl<int>::Tpl{}.print();
  typename Tpl<int>::Tpl<string>{}.print();
  typename Tpl<int>::Tpl<int>{}.print();
  typename Tpl<int>::Tpl::Tpl{}.print();
  typename Tpl<string>::Tpl<int>{}.print();
  typename Tpl<int>::Tpl::Tpl<int>{}.print();
  typename Tpl<string>::Tpl::Tpl<int>::Tpl{}.print();
  typename Tpl<int>::Tpl<string>::Tpl<int>::Tpl<string>::Tpl<int>::Tpl<string>{}.print();
}

that gives the following output

int
string
int
int
int
int
int
string

My question is how typename works in these cases, specially in the last one where you can switch between different template arguments. I would like to know if this behavior is standard or if it's some weirdness on the compiler.

This is the info of the compiler:

> g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
Edwin Rodríguez
  • 1,229
  • 10
  • 19

1 Answers1

9

This is because of [temp.local]/1:

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    Wow, Very interesting! So it's like every class is like `class SomeName { using SomeName = SomeName; };`? – Edwin Rodríguez Dec 13 '15 at 00:14
  • 2
    Kind of. See http://stackoverflow.com/questions/25549652/c-why-is-there-injected-class-name – Jonathan Wakely Dec 13 '15 at 00:15
  • @melak47: I'm not exactly sure whether the `typename` is required. I was only trying to explain why the name can be used as a typename and as a template name... – Kerrek SB Dec 13 '15 at 00:15
  • I think all uses of `typename` in the example are redundant. – Jonathan Wakely Dec 13 '15 at 00:23
  • @JonathanWakely: Clang says yes, GCC says [no](http://melpon.org/wandbox/permlink/UogZLFDr6NWFxLsM) :-S Given that constructors don't have names, I agree thought that there should be no ambiguity. I also wonder why you couldn't insert `template`, too. – Kerrek SB Dec 13 '15 at 00:24
  • 1
    Constructors don't have names, but the injected class name can still name the constructor. Isn't C++ wonderful :D – melak47 Dec 13 '15 at 00:58