为什么我们不能自动推断出退货类型?

Rei*_*ica 51 c++ c++11

最近我在找一个想要使C++更多Haskell-y的朋友,我们想要一个基本上像这样的函数:

auto sum(auto a, auto b) {
    return a + b;
}
Run Code Online (Sandbox Code Playgroud)

显然我不能使用auto作为参数类型,所以我将其更改为:

template<class A, class B>
auto sum(A a, B b) {
    return a + b;
}
Run Code Online (Sandbox Code Playgroud)

但这也不起作用.我们最终意识到我们需要这个:

template<class A, class B>
auto sum(A a, B b) -> decltype(a + b) {
    return a + b;
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,重点是什么?不decltype只是重复信息,因为编译器只能查看return语句?

我认为可能需要它,所以我们可以只包含一个头文件:

template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
Run Code Online (Sandbox Code Playgroud)

...但我们无论如何都不能使用这样的模板.

我考虑的另一件事是编译器可能更容易,但看起来它实际上会更难.

案例1:随着 decltype

  • 弄清楚decltype语句的类型
  • 找出任何返回值的类型
  • 看看它们是否匹配

案例2:没有 decltype

  • 找出任何返回值的类型
  • 看看它们是否匹配

因此,考虑到这些事情,尾随返回类型的重点是decltype什么?

小智 38

如果我们有以下内容怎么办?

template<class A, class B, class C>
auto sum(A a, B b, C c) {
   if (rand () == 0) return a + b;

   // do something else...

    return a + c;
}
Run Code Online (Sandbox Code Playgroud)

.. where a + ba + c表达式产生不同类型的结果.编译器应该决定将该函数作为该函数的返回类型以及为什么?C++ 11 lambdas已经涵盖了这种情况,只要return语句可以推导为相同的类型就可以省略返回类型(需要NB标准引用,一些来源声称只允许一个返回表达式,这是一个gcc毛刺).


技术原因是C++允许定义和声明是分开的.

template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);

template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
Run Code Online (Sandbox Code Playgroud)

模板的定义可能是在头中.或者它可能在另一个文件中,因此您在查看界面时不必浏览功能定义的页面和页面.

C++必须考虑所有可能性.将尾随返回类型限制为仅仅函数定义意味着您不能执行如下这样简单的操作:

template<class A, class B>
class Foo
{
  auto sum(A a, B b) -> decltype(a + b);
}

template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
Run Code Online (Sandbox Code Playgroud)

  • 从技术上讲,这是一个已解决的问题.Lambdas允许你省略返回类型,只要所有`return`语句的`decltype`是相同的.所以C++ 11已经有了解决这个问题的语言; 编译器将在lambda中报告错误,其中`return`s不一致.因此,单独这不是规范要求在命名函数上使用尾随返回类型的正当理由. (15认同)
  • @BrendanLong:不一定.可以进行隐式转换.但是没有显式类型编译器将不知道要转换什么以及为什么. (11认同)
  • 如果return语句返回不同的东西,函数将无论如何都不会编译. (2认同)
  • 没错,lambdas确定返回类型,如果你尝试返回不同的东西则会失败.也许实际上并没有什么能够将这种智能水平投射到编译器上...除了函数声明可能在SFINATE中起重要作用,并且没有明确指定类型可能效果不好...我不喜欢不知道. (2认同)

Nic*_*las 8

但我们无论如何都不能使用这样的模板.

首先,尾随返回类型不仅仅是模板事物.它们适用于所有功能.其次,说谁?这是完全合法的代码:

template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);

template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
Run Code Online (Sandbox Code Playgroud)

模板的定义可能是在头中.或者它可能在另一个文件中,因此您在查看界面时不必浏览功能定义的页面和页面.

C++必须考虑所有可能性.将尾随返回类型限制为仅仅函数定义意味着您不能执行如下这样简单的操作:

template<class A, class B>
class Foo
{
  auto sum(A a, B b) -> decltype(a + b);
}

template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
Run Code Online (Sandbox Code Playgroud)

这对许多程序员来说相当普遍.想要以这种方式编码没有任何问题.

lambda在没有返回类型的情况下离开的唯一原因是因为它们必须具有使用定义定义的函数体.如果将尾随返回类型限制为仅定义可用的那些函数,则无法使用上述任何一种情况.

  • @VladLazarenko:理论上没什么; Lambdas现在就做.除非lambda中不同类型的返回值不同,否则您不必指定返回类型.规范_could_允许它,但它也意味着不允许函数的前向声明. (2认同)

Ayj*_*jay 5

没有技术理由说明为什么不可能.他们没有的主要原因是因为C++语言移动速度非常慢,并且需要很长时间才能添加功能.

几乎可以用lambdas获得你想要的漂亮语法(但你不能在lambdas中使用templacised参数,再次没有充分的理由).

auto foo = [](int a, double b)
{
    return a + b;
};
Run Code Online (Sandbox Code Playgroud)

是的,在某些情况下无法自动推断出返回类型.就像在lambda中一样,可能只需要在那些不明确的情况下自己声明返回类型.

目前,这些限制非常随意,令人非常沮丧.另请参阅删除概念以增加挫败感.