【发布时间】:2016-10-10 03:53:19
【问题描述】:
随着 C++14 中 auto 返回类型的引入,是否存在需要尾随返回类型或在 C++14 和 17 中完全过时的实际情况?
【问题讨论】:
-
除了已经提到的,尾随返回类型还允许您在返回类型中使用
this。
随着 C++14 中 auto 返回类型的引入,是否存在需要尾随返回类型或在 C++14 和 17 中完全过时的实际情况?
【问题讨论】:
this。
考虑...
auto f(int x)
{
if (x == 2)
return 3;
return 2.1;
}
...这有一个模棱两可的返回类型 - int 或 double。显式返回类型(无论是前缀还是尾随)都可以消除歧义并将return 参数转换为返回类型。
如果您想在某些参数上使用decltype、sizeof 等,尾随返回类型也很有用:
auto f(int x) -> decltype(g(x))
{
if (x == 2)
return g(x);
return 2;
}
【讨论】:
decltype(g(x)),这意味着f(int) 应该返回g(int) 返回的任何类型。如果您不熟悉 decltype 文档,请在 Google 上搜索。我的观点是,您不能使用较旧的前导返回类型,例如 decltype(g(x)) f (int x) { ... },因为在编译器看到 g(x) 之前没有声明 x - 它只能在 int x 之后使用。
sizeof。
auto f(int x) -> std::array<bool, sizeof x> {...}
decltype(g(x)) 不会和decltype(g(0)) 一样吗?
尾随返回类型为您提供 SFINAE 支持。推导的返回类型永远不会过早导致错误,以至于仅仅是替换失败。
这允许编译器不必编译整个任意函数体然后干净地退出以确定是否应用了重载。
【讨论】:
除了您需要在哪里使用它(这里的其他答案给出了很好的例子),您可以将它用于清晰,明确说明函数返回的内容。一旦您意识到(希望很快)阅读代码至少与编写代码同样重要,这一点就非常重要。
考虑:
auto split(gsl::cstring_span str)
{
...
...
auto tokens = std::vector<gsl::cstring_span>();
...
...
for (...) {
...
...
...
}
...
return tokens;
}
对比:
auto split(gsl::cstring_span str) -> std::vector<gsl::cstring_span>();
{
... doesn't even matter
}
我不应该在实现中查看函数的契约是什么。通过查看第一个示例,我可能会猜到返回类型是什么,但这种假设在编程中是危险的。我必须扫描实现以确保在调用该函数时收到什么。对于第二个示例,我清楚地说明了界面。我不关心实现,因为名称是不言自明的,所以我根本不需要查看定义。
进一步考虑这个甚至最糟糕的例子。让我们看看这个函数返回什么:
auto split(const char* str)
{
return split(gsl::cstring_span(str));
}
好的,现在搜索那个重载:
auto split(gsl::cstring_span str)
{
return split_impl(str);
}
好吧...,现在让我们继续:
auto split_impl(gsl::cstring_span str)
{
return split_impl(str, ::isspace);
}
你是在开玩笑吗。
所以我希望你明白这一点。
我并不是说总是使用显式返回类型,我是说考虑一下应该通过简单的函数快速了解返回类型。有时,对于单线,可以从身体中发现。有时,可以从名称中安全地猜出它(例如is_empty() 显然返回bool)。其他时候,您需要明确命名它。让以后使用您的代码的开发人员的生活变得更简单,尤其是因为这个人最终将不可避免地成为您。
【讨论】: