1
#include <iostream>

template<class T> struct A {
    typedef T a; 
}; 
template<class T> 
struct B {
    typedef typename A<T>::a a;
    static a foo(a b); 
}; 
template<class T> 
a B<T>::foo(a b) {return b}

int main() {
    std::cout << B<int>::foo(1); 
}

Gives the following error: (try it).

main.cpp:13:1: error: 'a' does not name a type
    a B<T>::foo(a b) {return b}

An inline definition does not suffer from this error.

Could someone please explain why the compiler can not resolve a in this case, andhow I can make this code work.

I would like to not resolve all the names explicitly like

typename B<T>::a B<T>::foo(typename B<T>::a b) {return b}

As it would decrease readability.

marc
  • 6,103
  • 1
  • 28
  • 33
  • http://stackoverflow.com/questions/1643035/propagating-typedef-from-based-to-derived-class-for-template and http://stackoverflow.com/questions/1567730/inheritance-and-templates-in-c-why-are-methods-invisible seem related. – marc Jun 19 '15 at 14:53

2 Answers2

4

That's because the a here is still looking in global scope:

template<class T> 
a B<T>::foo(a b) {return b;}
^^

You're doing unqualifed lookup on a. Once you get to the B<T>:: part of the definition, that scope is added to all further lookup. So the type of the argument b will be looked up in the scope of B<T>.

You simply need to qualify it the return type:

template<class T> 
typename B<T>::a B<T>::foo(a b) {return b;}

The relevant rules is for why the argument type a can be found is in [basic.lookup.unqual]/8:

For the members of a class X, a name used in a member function body, in a default argument, in an exceptionspecification, in the brace-or-equal-initializer of a non-static data member (9.2), or in the definition of a class member outside of the definition of X, following the member’s declarator-id, shall be declared in one of the following ways:
— before its use in the block in which it is used or in an enclosing block (6.3), or
— shall be a member of class X or be a member of a base class of X (10.2), or

The return type a does not match the bolded text (or any of the text above), but the argument type a does.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • The argument type isn't in the member function body. It is "in the definition of a class member outside of the definition of X, following the member’s *declarator-id*". – T.C. Jun 19 '15 at 17:02
2

If C++11 and 14, you can declare your function auto to get rid of the long return type. You need to specify it as a trailing type in C++11, but this allows you to get omit the typename B<T>:: because the compiler already knows where to look.

//C++11
template<class T>
auto B<T>::foo(a b) -> a {return b;}

//C++14
template<class T>
auto B<T>::foo(a b) {return b;}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193