Does anyone know why template argume开发者_如何转开发nts are needed for return-types but not for argument-types when defining template-methods? An example:
template<typename T>
struct Car {
Car drive(Car); // will be defined after the template declaration.
};
// Attempt #1: does not compile.
// Error: use of class template Car requires template arguments
template<typename T>
inline Car Car<T>::drive(Car) {}
// Attempt #2: compiles!
// The only difference is the use of template argument in return type.
// However, note that the argument to func does not require template argument!
template<typename T>
inline Car<T> Car<T>::drive(Car) {}
Not sure why the template argument is needed for the return-type but not required for the argument-type. When Attempt #1 failed, I was expecting Attempt #2 to fail too and expected I would need:
template<typename T>
inline Car<T> Car<T>::drive(Car<T>) {} // but no need to go this far.
but Attempt #2 worked!
Is there good reason for this behavior?
First, you acknowledge this makes no sense: Car c;
, right? Car
must have template arguments. That's why you need to specify it on the return type, and on the class name.
But after the scope-resolution operator (::
), Car<T>
is injected as Car
*, so Car
is an alias to Car<T>
. But this only happens inside the scope of Car<T>
, which is why you need it everywhere else but not after ::
. Of course, you are free to explicitly specify the arguments yourself anyway.
*This feature is better explained like this:
template <typename T>
struct foo
{
// as if the compiler did this:
typedef foo<T> foo; // (of course, actually illegal)
};
foo
is available within the scope of foo<T>
as foo<T>
. After the scope-resolution operator, though, that scope is available for use, and the template arguments are optional.
This is because argument(s) type(s) of a method are deduced using class scope, but return type is deduced from the same scope where a class is defined if those methods are defined outside class scope. This is true for everything, not only templates. To add to your example, the following won't compile:
class Foo
{
typedef int Bar;
Bar foo () const;
};
Bar
Foo::foo () const
{
return 0;
}
... and to fix it you have to tell exactly that Bar is from Foo's scope:
Foo::Bar
Foo::foo () const
{
return 0;
}
From N3225, 3.4.1/8 (unqualified name lookup), a name used after a member function's declarator-id shall be looked-up inside the class definition first.
A name used in the definition of a member function (9.3) of class X following the function’s declarator-id (That is, an unqualified name that occurs, for instance, in a type or default argument expression in the parameter declaration- clause or in the function body.) or in the brace-or-equal-initializer of a non-static data member (9.2) of class X shall be declared in one of the following ways:
— before its use in class X or be a member of a base class of X (10.2), or
— if X is a nested class of class Y (9.7), before the definition of X in Y, or shall be a member of a base class of Y (this lookup applies in turn to Y ’s enclosing classes, starting with the innermost enclosing class), or
— if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or
— if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the definition of class X in namespace N or in one of N ’s enclosing namespaces.
Then injected-class-name is a normal class member for the purpose of name lookup, and injected-class-name is declared at the beginning of the class, before any other members.
3.3.2/7
The point of declaration for an injected-class-name (Clause 9) is immediately following the opening brace of the class definition.
3.4/3
The injected-class-name of a class (Clause 9) is also considered to be a member of that class for the purposes of name hiding and lookup.
Lastly, for template,
14.6.1/3
The injected-class-name of a class template or class template specialization can be used either with or without a template-argument-list wherever it is in scope.
Therefore,
template<typename T>
inline Car<T> Car<T>::drive(Car) {}
The first return type name Car cannot find injected-class-name, because it is looked up as a normal name and will find the global class Car.
But the second parameter type Car can find injected-class-name, because it is after the function's delcarator-id. Injected-class-name can be used with or without bracket T bracket.
So you got the answer now.
精彩评论