繁体   English   中英

在C ++ 11中省略返回类型

[英]Omit return type in C++11

我最近发现自己在C ++ 11模式下使用gcc 4.5的以下宏:

#define RETURN(x) -> decltype(x) { return x; }

并编写如下函数:

template <class T>
auto f(T&& x) RETURN (( g(h(std::forward<T>(x))) ))

我一直这样做是为了避免不得不有效地编写函数体两次,并保持身体和返回类型的同步(这在我看来是等待发生的灾难)的不便。

问题是这种技术只适用于一行功能。 所以当我有这样的事情时(复杂的例子):

template <class T>
auto f(T&& x) -> ...
{
   auto y1 = f(x);
   auto y2 = h(y1, g1(x));
   auto y3 = h(y1, g2(x));
   if (y1) { ++y3; }
   return h2(y2, y3);
}

然后我必须在返回类型中添加一些可怕的东西。

此外,每当我更新函数时,我都需要更改返回类型,如果我没有正确更改它,如果我很幸运,我会收到编译错误,或者更坏的情况下会出现运行时错误。 必须将更改复制并粘贴到两个位置并保持同步,我觉得这不是一个好习惯。

而且我想不出一种情况,我希望在返回时使用隐式强制转换而不是显式强制转换。

当然有一种方法可以要求编译器推断出这些信息。 编译器保守秘密有什么意义? 我认为C ++ 11的设计不需要这样的复制。

似乎g ++ 4.8正在实现自动返回类型推导。 这个补丁由杰森·梅里尔(Jason Merrill)投入,他也在发送C ++的论文 - 1Y用于该功能。 该功能可用-std = c ++ 1y。

还在玩它。

这种行为的基本原理在草案8.3.5p12中给出:

对于在declarator-id之前指定更复杂的类型,trailing-return-type最有用:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

而不是

template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);

所以这实际上只是为了简化引用参数名称有帮助的情况。

如果你假设C ++ 能从函数体中推断函数的返回类型:这不会飞。 C ++(和C)的目标是通过将声明与实现分离来实现模块化,因此在调用时,您可能没有可用的函数体。 但是,每个调用者都需要知道所调用的每个函数/方法的参数类型和返回类型

如果您只是尝试设置返回类型,请将其设置为模板参数。 这样,您可以更改与返回类型相关的所有内容,而无需实际更改功能。 如果您希望在此示例中使用,则可以设置默认返回类型。

template <class R = int, class T>
R f(T&& x)
{
   ...
   return h2(y2, y3);
}

下面的代码证明了它的有效性。

演示代码:

#include <iostream>
#include <iomanip>

template <class T, class S>
T h2(T& x, S& y)
{
  return x + y;
}

template <class R = int, class T>
R f(T& x)
{
  auto y2 = x;
  auto y3 = x;
  return h2(y2, y3);
}

int main(int argc, char** argv)
{
  int x = 7;
  std::string str = "test! ";

  auto d = f<double>(x);
  auto i = f(x); // use default type (int)
  auto s = f<std::string>(str);

  std::cout << std::fixed << std::setprecision(4);
  std::cout << "double: " << d << std::endl;
  std::cout << "int: " << i << std::endl;
  std::cout << "string: " << s << std::endl;

  return 0;
}

OUTPUT:

double: 14.0000
int: 14
string: test! test!

不幸的是,您正在寻找的确切功能尚未存在,并且不是C ++ 0x规范的一部分。 但是,在草拟时,这可能是C ++ 1x规范的一部分。 在那之前,坚持模板。

编辑:oops,我刚刚意识到trailing-return-type说明符和return语句之间存在范围差异。 特别:

auto f(int a)
{
    char r[sizeof(f(a))+1];
    return r;
}

KABOOM!


上一个答案:

遗憾的是,在这种情况下,语言不提供编译器推断返回类型的语法,因为显示推理是可能的很简单。

具体来说,我们讨论的是函数中只有一个return语句的情况。

与返回语句的函数位置或前面的代码有多复杂无关,应该清楚以下转换是可能的:

return (ugly expression);

auto return_value = (ugly expression);
return return_value;

如果编译器可以推断return_value的类型(并且根据C ++ 0x规则,它可以),那么可以选择推断类型的return_value作为函数的返回类型。

因此,在我看来,对C ++ 0x进行修改时,只有在返回语句的多重性不是一个时才需要尾随返回类型说明符才是可行的并且可以解决问题。

我同意Yttrill。 返回类型推导已经被证明是像Haskell这样的语言的可行实践,并且由于C ++已经实现了'auto',它只是进一步实现返回类型推导。 这种推论应该在专业化时发生,而不是模板定义,因为需要提供给模板的真实类型的信息。 声明和定义的分离不再是通用C ++中的常见做法,因为模板体必须写在头文件中,因此模板体几乎总是使用模板声明。 在存在多个返回语句并且它们的类型不匹配的情况下,编译器可以愉快地报告错误。 总之,如果委员会愿意,在C ++中完全可以使用返回类型推导。 并且它非常重要,因为手动编写返回类型的重复阻碍了小型通用辅助函数的普遍使用,这在功能和通用编程中是常见的做法。

许多编程语言(包括Ocaml和Felix)都可以推断出函数的返回类型,并且不需要指定它。 在Ocaml中,您可以并且必须在界面中指定它。 在Felix中我找到了一些函数,明智的做法是在库代码中指定它以使其更易于使用。

我很惊讶“自动”不适用于返回类型,它当然是在盘子上。 这被认为太难实施了吗? [鉴于函数可以递归,这并非易事]。

哦......现在我明白了。 问题是愚蠢的模板设计功能“依赖名称”:

template<class T> auto f(T a) { return a.f(); }

因此,虽然由于重载而计算普通函数的返回类型有点棘手,但由于依赖名称查找而无法使用模板:它只能在实例化之后完成。 但是在此之前必须发生超载。 因此,自动返回类型不能使用该语言,因为它不会泛化为模板。

您的退货类型是否真的经常改变? 为什么不能明确指定它? 例:

template<class T>
int f(T&& x)
{
...
}

人们做了二十多年......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM