为什么在C++中需要使用模板关键字?

3

一个函数模板:

template<class T> T 
max(T a, T b){return (a > b)? a: b;}

何时使用:


max<int>(a, b); // Yeah, the "<int>" is optional most of the time.

但是如果您允许的话,我们可以这样编写模板:
T max<class T>(T a, T b){return (a > b)? a: b;} 
//I know the return type T is not in its scope, don't focus on that.

因此,我们可以像普通函数一样保持相同的声明和使用形式,甚至不需要引入和输入“template”关键字。我想类模板也是一样的吧?那么,有没有其他原因使模板成为我们今天所知道的形式呢?
我更改了形式,以便您不会关注返回类型:
auto max<class T>(T a, T b) -> T {return (a > b)? a: b;}
//This is C++11 only and ugly i guess. 
//The type deduce happens at compile time 
//means that return type really didn't to be a problem.

这只是选择的语法。我相信背后没有真正的强制性原因。这只是一个简单的决定。 - user703016
2
你太晚了,仅仅晚了几个月他们就接受了C++11。现在你必须等待下一个20年。 - Luka Rahne
一个问答网站并不适合主观性问题。但是你不是唯一一个提出这个问题的人。David Abrahams(MPL背后的那个家伙)有一篇有关模板代码更短语法的有趣博客文章:http://cpp-next.com/archive/2011/11/having-it-all-pythy-syntax/ 他甚至在clang邮件列表上写了一封信,寻求帮助改变解析器/语义分析来尝试这种语法,并检查它是否会引起歧义等问题 :) - Matthieu M.
@MatthieuM。了解原因会更好。有时我不明白为什么需要声明(许多语言不需要),但当我知道答案是为了帮助而不是干扰时,我在编码时会有更好的心情,也知道如何编写良好的代码。 - user955249
@Mike:我同意,但是你的问题措辞让我觉得它更像是对新语法的抱怨,而不是一个真正的询问问题。这只是我的印象,显然。 - Matthieu M.
显示剩余2条评论
4个回答

2
我立刻能想到的回答是:仅仅因为提出模板规范的人这样说了而且标准委员会中没有人觉得打那额外的8个字符会增加负担。
另一方面,模板的语法本来就很复杂、令人畏惧,template关键字的存在可以让代码阅读者更直观地知道他们正在处理模板,而不是C ++或任何实现特定结构(读编译器扩展)提供的其他东西。

2

在使用前必须声明T,因此它确实必须存在。

<class T> T max(T a, T b){return (a > b)? a: b;} 

但是,这里并不清楚 <class T> 是什么 - 编译器很可能会对此感到困惑。在前面加上 template 可以明确表明 < 不是运算符,而是括号内包含类型声明。
从这个角度来看,你的第二个例子应该是可行的,但请记住,这种语法仅适用于 C++11,并且模板比它早引入。

有一些编程语言根本不需要“template<class T>”,它们也能很好地运行。 - hamstergene
2
Scala,Rust。还有很多,我只是记不起来了。这绝对不是必须的。顺便说一句,在C++中有时可以在声明之前使用标识符(考虑内联类方法相互调用:它们不需要前向声明),因此即使对于C++,“T必须先声明”听起来也像是一个薄弱的理由。 - hamstergene
很好的观察,但我认为这些在C++中是例外。通常情况下,至少在C++中访问之前必须先声明。当然,其他编程语言并没有这种限制。 - cschwan
@cschwan 把函数作为整体和类一样并不会有任何问题。在使用前声明只是为了确保不会因为意外引入新的名称(比如拼写错误或者其他原因),这种语法并不违反规则。 - user955249

1

我认为问题在于编译器实现的易用性:C++中使用的任何名称都必须在使用之前至少声明,以帮助编译器能够从开头解析(出于我不知道的原因)。这就是你需要使用奇怪的新语法来声明函数的原因,它允许在参数后定义返回类型。

所以,在阅读您的示例时,T是第一个使用的名称,但它没有被先声明,因此编译器不知道它是什么或者它属于哪种表达式。


1

你用那种方法很快就会遇到解析问题:

template <typename> int f(); // Current declaration syntax, type template argument.
template <int> int f();      // Current declaration syntax, non-type template argument.

void f<class>(); // New declaration syntax, type argument. 
void f<int>(); // New declaration syntax, non-type argument.
(void) f<int>(); // (void) is a cast , f is instantiation of f<class> with type int.

这不是问题。如果它单独存在,(void) f<int>();将被视为与template<int> void f();相同。当用作rvalue时,它将被视为转换。普通函数也会遇到同样的问题。 - user955249
@Mike:尝试使解析完全无歧义。f<int> f<int> ();怎么样?f<int> (*f<int>) ();(f<int>*) (*f<int>) (); - MSalters
f<int> 应该是一种预定义类型,因为它右侧没有括号。我不确定类名和函数名是否可以重载。如果允许这样做,它将成为一个函数模板,就像 template<int> f<int> f(); 一样,第二个将是指向第一个的指针,第三个与第二个相同,只是当它们单独存在时返回类型不同。而且没有办法使它们中的任何一个成为转换。 - user955249

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接