开发者

Template Arguments Needed in Return Types during Method Definition

开发者 https://www.devze.com 2023-02-13 20:17 出处:网络
Does anyone know why template argume开发者_如何转开发nts are needed for return-types but not for argument-types when defining template-methods?An example:

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号