c++ 14中的尾随返回类型

Trailing return type in C++14

本文关键字:返回类型 c++      更新时间:2023-10-16

随着c++ 14中auto返回类型的引入,是否存在需要尾随返回类型或者在c++ 14和c++ 17中完全过时的实际情况?

考虑…

auto f(int x)
{
    if (x == 2)
        return 3;
    return 2.1;
}

…它有一个模棱两可的返回类型——intdouble。显式返回类型(无论是前缀还是结尾)可以消除它的歧义,并将return参数强制转换为返回类型。

如果你想在某些参数上使用decltype, sizeof等,

尾随返回类型也特别有用:

auto f(int x) -> decltype(g(x))
{
    if (x == 2)
        return g(x);
    return 2;
}

尾随返回类型为您提供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)。其他时候,您需要显式地命名它。使以后使用您代码的开发人员的生活更简单,特别是因为那个开发人员最终将不可避免地是您。