10

实际上是否有任何理由不再使用以下语法:

template<typename T>
auto access(T& t, int i)
  -> decltype(t[i])
{
    return t[i];
}

现在我们可以使用:

template<typename T>
decltype(auto) access(T& t, int i)
{
    return t[i];
}

尾随返回类型语法现在似乎有点多余?

4

2 回答 2

19

推导的返回类型对 SFINAE 不友好。t[i]如果无效,此重载将简单地退出重载集:

template<typename T>
auto access(T& t, int i)
  -> decltype(t[i])
{
    return t[i];
}

而这种重载不会,导致硬错误:

template<typename T>
decltype(auto) access(T& t, int i)
{
    return t[i];
}

演示


此外,您可能会遇到推导返回类型冲突的问题。考虑我是否想返回一个std::optional<T>. 以下代码无法编译,因为与以下代码std::nullopt_t不同std::optional<T>

#include <optional> // C++17 standard library feature

template <typename T>
auto foo(T const& val)
{
    if (val.is_invalid()) return std::nullopt;
    return val.some_function_returning_an_optional();
}

尾随返回类型可让您准确指定要返回的表达式类型:

template <typename T>
auto foo(T const& val)
    -> decltype(val.some_function_returning_an_optional())
{
    if (val.is_invalid()) return std::nullopt;
    return val.some_function_returning_an_optional();
}

您可以使用前导返回类型,但它需要使用std::declval,这使得它更难理解:

template <typename T>
decltype(std::declval<T const&>().some_function_returning_an_optional())
foo(T const& val)
{
    if (val.is_invalid()) return std::nullopt;
    return val.some_function_returning_an_optional();
}

演示

于 2018-08-31T22:49:00.017 回答
7

是的,至少三个原因:

  1. 有意义的声明:您的第一个变体有一个声明,它告诉我返回类型是什么;您的第二个变体要求我阅读您的定义。但是您的定义可能在另一个文件中,或者不是很清楚。
  2. 类型约束或类型转换:您的主体可能返回的不是表达式T[i],因此您会获得类型约束或从主体返回的内容到您想要获得的内容的转换。
  3. 向后兼容性:这对您来说可能看起来微不足道,但是尝试编写一个库并告诉您的用户“哦,由于我可爱的语法选择,您需要一个符合 C++14 的编译器”。

贾斯汀的回答还有第四个原因。

于 2018-08-31T22:48:47.697 回答