方法定义期间返回类型中需要的模板参数

时间:2011-03-01 00:11:35

标签: c++ templates

有没有人知道为什么返回类型需要模板参数,而在定义模板方法时不需要参数类型?一个例子:

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) {}

不确定为什么返回类型需要模板参数,但参数类型不需要。当尝试#1失败时,我期待尝试#2也失败并且预计我需要:

template<typename T>
inline Car<T> Car<T>::drive(Car<T>) {}  // but no need to go this far.

但尝试#2工作了!

这种行为有充分的理由吗?

3 个答案:

答案 0 :(得分:9)

首先,你承认这没有意义:Car c;,对吧? Car必须有模板参数。这就是你需要在返回类型和类名上指定它的原因。

但是在范围解析运算符(::)之后,Car<T> 注入Car *,因此Car是别名Car<T>。但这只发生在Car<T>的范围内,这就是为什么你需要它在其他地方而不是在::之后。当然,您可以自由地自己明确指定参数。


*此功能更好地解释如下:

template <typename T>
struct foo
{
    // as if the compiler did this:
    typedef foo<T> foo; // (of course, actually illegal)
};

foofoo<T>作为foo<T>的范围内可用。但是,在范围解析运算符之后,该范围可供使用,并且模板参数是可选的。

答案 1 :(得分:2)

这是因为方法的参数类型是使用类范围推导出来的,但是返回类型是从定义类的同一范围推导出来的,如果这些方法是在类范围之外定义的。这对所有事情都是如此,不仅仅是模板。要添加到您的示例中,以下内容将无法编译:

class Foo
{
    typedef int Bar;

    Bar foo () const;
};

Bar
Foo::foo () const
{
    return 0;
}

...要解决这个问题,你必须确切地说Bar是来自Foo的范围:

Foo::Bar
Foo::foo () const
{
    return 0;
}

答案 2 :(得分:0)

从N3225,3.4.1 / 8(非限定名称查找),首先在类定义中查找成员函数的declarator-id之后使用的名称。

  

a的定义中使用的名称   X类的成员函数(9.3)   在函数的declarator-id之后   (也就是说,例如,在参数声明中的类型或默认参数表达式中出现的非限定名称 -   条款或在函数体中。)或在   支撑或平等初始化器   类的非静态数据成员(9.2)   X应在其中一个中声明   以下方式:

     

- 在X级使用之前或者是   基类X(10.2)或

的成员      

- 如果X是Y类的嵌套类   (9.7),在X的定义之前   Y,或者应该是基地的一员   Y的类(此查找适用于   转到Y的封闭课程,   从最里面的封闭开始   ()或

     

- 如果X是本地类   (9.8)或是本地的嵌套类   class,在定义类之前   包含定义的块中的X.   X类,或

     

- 如果X是其成员   namespace N,或者是一个嵌套类   作为N的成员的类,或者是   本地类或一个嵌套类   一个函数的本地类是一个   N的成员,在定义之前   名称空间N中的类X或N中的一个   封闭的命名空间。

然后,为了名称查找,inject-class-name是一个普通的类成员,并且在类的开头,在任何其他成员之前声明了inject-class-name。

3.3.2 / 7

  

宣言的要点   注入类名(第9条)是   开幕后立即   大括号的定义。

3.4 / 3

  

类的注入类名   (第9条)也被认为是   为此目的的那一类成员   名称隐藏和查找。

最后,对于模板,

14.6.1 / 3

  

类的注入类名   模板或类模板   专业化可以用于   或没有template-argument-list   无论它在哪个范围内。

因此,

template<typename T>
inline Car<T> Car<T>::drive(Car) {}

第一个返回类型名称Car找不到inject-class-name,因为它被查找为普通名称,并且会找到全局类Car。

但是第二个参数类型Car可以找到inject-class-name,因为它位于函数的delcarator-id之后。 Injected-class-name可以使用或不使用括号T括号。

所以你现在得到了答案。