【问题标题】:Legitimate uses of the trailing return type syntax as of C++14从 C++14 开始合法使用尾随返回类型语法
【发布时间】:2019-02-06 22:49:56
【问题描述】:

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

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];
}

尾随返回类型语法现在看起来有点多余?

【问题讨论】:

  • 也许只有我一个人,但有时我想查看函数定义并立即知道它将返回什么而不查看实现
  • 您不是唯一一个,C++ 编译器也经常这样......他们很容易与新标准功能的早期实现混淆。 (或者更确切地说:编译器 A 在编译器 B 理解的代码上出错,反之亦然......)

标签: c++ c++14 trailing-return-type return-type-deduction


【解决方案1】:

推导的返回类型对 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];
}

Demo


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

#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();
}

Demo

【讨论】:

  • 第二个例子不好,因为你可以做正常的前导返回类型。我觉得只剩下表达 sfinae 了。
  • @NirFriedman 这是一个简化的例子,但我遇到了一个可以救我的案例。返回类型取决于参数,但它也是可选的。可以使用领先的返回类型,但一点都不好。我会改进第二个例子
  • 好吧,当我们有了概念并彻底使用它们时,return-type-deduction 将会上升,因为我们通过它来获得 SFINAE。它可能只需要几十年。
  • @Deduplicator 可能视情况而定。对于临时约束,-&gt; decltype(t[i])requires requires(T const&amp; v, int i) { v[i]; } 短得多。但是如果某些约束使用得足够多,它们可能会变成命名概念,这样就可以正常工作了。
【解决方案2】:

是的,至少有三个原因:

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

Justin's answer 还有第四个原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-26
    • 2013-05-11
    • 1970-01-01
    • 2019-01-26
    • 2019-10-07
    • 2015-09-10
    • 1970-01-01
    相关资源
    最近更新 更多