有没有人知道为什么返回类型需要模板参数,而在定义模板方法时不需要参数类型?一个例子:
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工作了!
这种行为有充分的理由吗?
答案 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)
};
foo
在foo<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括号。
所以你现在得到了答案。