views:

162

answers:

4

It's hard to get a word for this. Sometimes I see a class like this:

template <typename T>
class Wrapper
{
public:
    Wrapper(const T& t) : t_(t) {}
    Wrapper(const Wrapper& w) : t_(w.t_) {}
private:
    T t_;
}

As far as I can tell this is legitimate code. However, why is the copy constructor allowed to accept a const Wrapper& without explicitly stating that it needs a const Wrapper<T>&. When else is the template type implied? Is it allowed to write the copy constructor this way if you don't use an in-class definition?

+3  A: 

Basically, inside a class template definition you are allowed to use the template-name for the template being defined as a short hand for the full parameterized version of the template-id.

Charles Bailey
A: 

Within a templated class like this using the name of the class is the same as using Wrapper<T>.

Brett Hall
+3  A: 

It is explicitly specified by the language standard in 14.6.1/1:

Within the scope of class template, when the name of the template is neither qualified nor followed by <, it is equivalent to the name of the template followed by the template-parameters enclosed in <>.

This was re-worded (through the concept of "injected class name") in the later versions of the standard, but the point is that this behavior is explicitly spelled out in the document.

To answer the second part of your question, this rule also applies to parameter declarations when writing out-of-class method definitions, but it doesn't apply to the return type declarations. For example, this code is OK

template <typename T> struct S {
  S foo(S);
};

template <typename T> S<T> S<T>::foo(S s) {
  /* whatever */
}

but you can't remove the <T> bit from the return type in the definition of the method. (And you can't remove <T> from the qualified name of the method.)

As for the constructior specifically: your should use the full name (with <T>) for the class, but you should not use <T> in the name of the constructor itself. So the shortest form for out-of-class definition in your case would be

template <typename T> Wrapper<T>::Wrapper(const Wrapper& w) : t_(w.t_)
{
}

Note, that you can't add the <T> bit to the constructor name even if you want to

template <typename T> Wrapper<T>::Wrapper<T>(const Wrapper& w)
                                         ^ ERROR !!!

P.S. This last claim needs further research. Comeau Online compiler thinks it is an error, while GCC thinks it is OK. I'll return to it later.

P.P.S. The compiler in MSVC++ 2005 complains about the latter declaration with a warning

warning C4812: obsolete declaration style: please use 'Wrapper<T>::Wrapper' instead

Interesting...

AndreyT
I tried this with GCC 4.4.2, and it rejects with "main1.cpp:4: error: invalid use of constructor as a template". I've send a response to your usenet question, i think it should appear soon, with some thoughts on this stuff.
Johannes Schaub - litb
A: 

No, out of class you have to write:

template <typename T>
Wrapper<T>::Wrapper<T>(const Wrapper<T>& w) : t(w.t) {}

But inside the class, Wrapper stands in for the "current class".

EDIT: This is for Andrew and his investigation into why a compiler would reject the on the constructor:

template <typename T>
class Foo {
public:
    Foo() {}
    Foo(const Foo& f);
};

template <typename T>
Foo<T>::Foo<T>(const Foo<T>& f) { }

int main(int argc, char** argv)
{
    Foo<int> f;
    Foo<int> g(f); // make sure the template is instantiated
}
jmucchiello
There's no need to use `Wrapper<T>` in the parameter declaration. Just `Wrapper` is perfectly enough.
AndreyT
Actually, the above is incorrect. It is always illegal to use the `<T>` in the constructor name itself. It should be 'Wrapper<T>::Wrapper(...`.
AndreyT
Always? Compiles just fine in MSC6. I don't have access to gcc at work, lamentably.
jmucchiello
Try Comeau Online http://www.comeaucomputing.com/tryitout . The `<>` after the constructor name would indicate that the constructor itself is a template (a template constructor within a template class). It is not in this case, so the `<>` part is not allowed.
AndreyT
Hmm... GCC actually accepts it. I might be wrong. Let me look further into it...
AndreyT
Comeau accepts it too on their online compiler. This is because of the way that injected-class-names work in templates (an injected-class-name is the term for the special name for a type visible in its own scope, to prevent name lookup from finding another class). The injected-class-name in a template refers to the class template, but if it's used without a template argument list, it refers to whatever version of the template is being currently instantiated - so inside `Wrapper<T>`, `Wrapper` is a synonym for `Wrapper<T>`. Arguably, naming the constructor `Wrapper<T>()` is more correct.
coppro
I just tried another variant again. No, Comeau Online *does not* accept it.
AndreyT
I think we can conclude there is no definitive answer for this.
jmucchiello
There is, of course. C++ is standardized and the definitive answer is there, in the standard. Frankly, from what I see in the standard, the `<T>` form shoudl be legal. What I don't see a definitive answer for is why Comeau rejects it.
AndreyT
I'll edit my answer with the full test I ran...
jmucchiello