views:

140

answers:

3

I have a class like this

template< typename T >
class vector {
  public:
    typedef T &       reference;
    typedef T const & const_reference;
    typedef size_t    size_type;

    const_reference at( size_t ) const;
    reference at( size_t );

and later in the same file

template< typename T >
typename vector<T>::const_reference    // Line X
vector<T>::at( size_type i ) const
{
    rangecheck();
    return elems_[ i ];
}


template< typename T >
reference                              // Line Y
vector<T>::at( size_type i )
{
    rangecheck();
    return elems_[ i ];
}

Line X compiles fine but Line Y does not compile. The error message from g++ (version 4.4.1) is:

foo.h:Y: error: expected initializer before 'vector'

From this I gather that, if I want to have non-inline functions then I have to fully qualify the typedef name as in Line X. (Note that, there is no problem for size_type.)

However, at least to me, Line X looks clumsy.

Is there any alternative approach?

+2  A: 

You are correct; the type needs to be qualified, because your typedefs have been given in the scope of the class and not in the global scope. I do not know of any way to beautify that without polluting the global scope, which would be a bad idea. But, it looks like your functions are very short, so why don't you simply define them inline? That would save you lots of extra typing and, IMHO, it's easier to read.

Michael Aaron Safyan
+4  A: 

Yes, in out-of-class member function definition you have to use a fully-qualified name for the nested return type. This, BTW, has nothing to do with templates. It is that way with non-template classes as well.

In your case it is a template class and, as a consequence of that, since you are using a qualified name to refer to a nested type in a dependent template class, you have to prepend it with keyword typename.

However, you only need to qualify the return type. Parameter types don't need to be qualified

template< typename T >
typename vector<T>::const_reference
  vector<T>::some_method( const_reference r ) const
{
  // ...
}

You can choose to use a qualified name for the parameter type as well, but in that case you will have to do the same thing as with the return type (for the very same reason): prepend it with typename keyword

template< typename T >
typename vector<T>::const_reference
  vector<T>::some_method( typename vector<T>::const_reference r ) const
{
  // ...
}
AndreyT
@AndreyT: Parameter types do need to be qualified if they depend on the template parameter.
Eddy Pronk
@Eddy Pronk: Interesting. I couldn't find a definitive answer in the standard. Yet, Comeau Onine successfully finds the *unqualified* type name from the parameter list inside the template class (suggesting that I'm right). G++ also does it the same way. Can you quote the place in the standard that would support your claim? I'm not saying you are wrong. Moreover, I would say that what you are saying makes perfect sense and is a logical thing to expect. Yet, the compilers disagree.
AndreyT
@AndreyT: I didn't try to find this in the standard yet. I looked at Effective C++ Item 42. See my answer.
Eddy Pronk
@Eddy Pronk: What I see in the standard is that it says that *when/if* you are using a *qualified* name to refer to a nested type of a template class, you have to add `typename` keyword. But it doesn't say that you *have* to use qualified names all the time. Quite the opposite, there are contexts when *unqualified* name is OK and I stongly suspect/believe that the parameter list in out-of-class method definition is one of them.
AndreyT
@Eddy Pronk: So, I believe that Item 42, if taken literally, is incorrect. Item 42 probably meant to say: "anytime you refer to a nested dependent type name in a template *using a qualified name*, you must immediately precede it by the word `typename`." But in contexts where an unqualified name is allowed, you can use that unqualified name without the `typename` keyword.
AndreyT
I don't see why they would need to be qualified. @EddyPronk, what's your reasoning?
Johannes Schaub - litb
Worth noting that `vector<T>::const_reference` doesn't need a `typename` prefix in C++0x anymore. `some_method` is a member of `vector<T>`, and `vector<T>` is supposed-to-be the injected-class-name (but i think there is a defect here - i will send a FCD comment today). So `vector<T>::const_reference` is a member of the current instantiation.
Johannes Schaub - litb
I guess with "nested" they just mean "qualified", though.
Johannes Schaub - litb
@Johannes: My point is the statement "Parameter types don't need to be qualified" is wrong. They do IF they depend on the template argument. "Nested" does not mean "qualified". In my answer size_type is a nested type of Foo.
Eddy Pronk
@Eddy i'm not saying what "nested" means (i do agree with you on its meaning), but i'm saying what the book authors possibly mean by "nested". Most often, book authors don't use standard language, but popular language instead. You probably find good books that use the word "header" for non-standard headers, but strictly speaking they should say "source file". I have the suspicion the same is true about this word "nested".
Johannes Schaub - litb
@Eddy yes in your case the "typename" is needed. But in your case, "T::foo" is already a qualified name, you don't need to make it qualified first. My point (and i suspect the one of AndreyT's likewise) is that *unqualified* dependent names don't need to be made qualified artificially. C++03 for example makes "B" in the following example a dependent name and it's a nested class name: `template<typename T> struct A { struct B { }; void f(B); }; template<typename T> void A::f(B) { }` there is no point in saying `typename A::B`. You can just say `B` alone.
Johannes Schaub - litb
+3  A: 

From Effective C++ Item 42: "anytime you refer to a nested dependent type name in a template, you must immediately precede it by the word typename." size_t is not nested inside anything dependent on a the template parameter. You did typedef it but it will always be size_t.

This is what it would look like it size_type DID depend on the template parameter.

#include <cstddef>

template< typename T >
class vector {
  public:
    typedef T &       reference;
    typedef T const & const_reference;

    const_reference at( typename T::size_type ) const;
    reference at( typename T::size_type );
};

template< typename T >
typename vector<T>::const_reference
vector<T>::at( typename T::size_type i ) const
{
    // ...
}

template< typename T >
typename vector<T>::reference
vector<T>::at( typename T::size_type i )
{
    // ...
}

struct Foo
{
    typedef size_t size_type;
};

int main()
{
    vector<Foo> f;
    return 0;
}

size_type is a nested type of Foo.

Eddy Pronk