Luca's meaningless thoughts  

C++ template WTF

by Leandro Lucarella on 2010- 07- 25 23:22 (updated on 2010- 07- 25 23:22)
tagged c++, d, en, programming, template, wtf - with 0 comment(s)

See this small program:

template<typename T1>
struct A {
        template<typename T2>
        void foo_A() {}
};

template<typename T>
struct B : A<T> {
        void foo_B() {
                this->foo_A<int>(); // line 10
        }
};

int main() {
        B<int> b;
        b.foo_B();
        return 0;
}

You may think it should compile. Well, it doesn't:

g++ t.cpp -o t
t.cpp: In member function ‘void B<T>::foo_B()’:
t.cpp:10: error: expected primary-expression before ‘int’
t.cpp:10: error: expected ‘;’ before ‘int’

Today I've learned a new (horrible) feature of C++, foo_A is an ambiguous symbol for C++. I've seen the typename keyword being used to disambiguate types before (specially when using iterators) but never a template. Here is the code that works:

template<typename T1>
struct A {
        template<typename T2>
        void foo_A() {}
};

template<typename T>
struct B : A<T> {
        void foo_B() {
                this->template foo_A<int>();
                //    ^^^^^^^^
                // or: A<T>::template foo_A<int>();
                // but not simply: template foo_A<int>();
        }
};

int main() {
        B<int> b;
        b.foo_B();
        return 0;
}

Note how you have to help the compiler, explicitly saying yes, believe me, foo_A is a template because it has no clue. Also note that the template keyword is only needed when A, B and A::foo_A are all templates; remove the template<...> to any of them, and the original example will compile flawlessly, so this is a special special special case.

Yeah, really spooky!

In D things are more natural, because templates are not ambiguous (thanks to the odd symbol!(Type) syntax), you can just write:

class A(T1) {
        void foo_A(T2)() {}
}

class B(T) : A!(T) {
        void foo_B() {
                foo_A!(int)();
        }
}

void main() {
        B!(int) b;
        b.foo_B();
}

And all works as expected.