在C ++ 11中省略返回types
我最近发现自己在C ++ 11模式下使用gcc 4.5使用下面的macros:
#define RETURN(x) -> decltype(x) { return x; }
并写这样的function:
template <class T> auto f(T&& x) RETURN (( g(h(std::forward<T>(x))) ))
我一直在这样做,以避免不必要有效地写两次函数体,并保持身体和返回types的同步(这在我看来是一个等待发生的灾难)的变化。
问题是,这种技术只适用于一行function。 所以当我有这样的事情(令人费解的例子):
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); }
那么我不得不把可怕的东西放在返回types中。
此外,无论何时我更新函数,我都需要更改返回types,如果我没有正确地更改它,那么如果我运气好的话,会得到一个编译错误,或者在最坏的情况下会出现运行时错误。 不得不复制和粘贴更改到两个位置,并保持同步我觉得不是好的做法。
而我不能想到一个情况,我想要隐式强制转换而不是显式强制转换。
当然,有一种方法可以让编译器推断出这些信息。 编译器保密是什么意思? 我认为C ++ 11的devise是这样的,所以不需要重复。
看来g ++ 4.8正在实现自动返回types的扣除。 这个补丁是由Jason Merrill发布的,他也正在发送一篇关于C ++ – 1Y的论文。 该function可用-std = c ++ 1y。
还在玩呢。
这个行为的基本原理在草案8.3.5p12中给出:
尾随返回types对于在declarator-id之前指定的types是非常有用的:
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 ++ 总是可以从函数体中推断函数的返回types:这不会飞。 C ++(和C)的目标是通过将声明与实现分离来实现模块化,所以在调用的时候,可能没有可用的函数体。 但是,每个调用者都需要知道被调用的每个函数/方法的参数types和返回types 。
如果您只是试图设置返回types,请将其作为模板参数。 这样,您可以更改与返回types相关的所有内容,而无需实际更改该function。 如果你想在这个例子中,你可以把一个默认的返回types。
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!
不幸的是,你正在寻找的确切function不存在(还),不是C ++ 0x规范的一部分。 但是,这可能是C ++ 1x规范草案的一部分。 在此之前,坚持模板。
编辑:哎呀,我只是意识到有一个跟踪返回types说明符和返回语句之间的范围区别。 特别:
auto f(int a) { char r[sizeof(f(a))+1]; return r; }
KABOOM!
先前的回答:
不幸的是,在这种情况下,语言没有提供让编译器推断返回types的语法,因为显示推理是可能的,这是微不足道的。
具体而言,我们正在讨论函数内部只有一个return语句的情况。
独立于return语句所在的函数的位置,或者前面的代码有多复杂,应该清楚的是,下面的转换是可能的:
return (ugly expression);
成
auto return_value = (ugly expression); return return_value;
如果编译器可以推断return_value
的types(并且可以根据C ++ 0x规则),则可以将返回值的推断typesselect为函数的返回types。
因此,在我看来,修改C ++ 0x时,只有当返回语句的多重性不完全相同时,才需要尾随返回types说明符,这样才能解决问题。
我同意Yttrill。 返回types的演绎在Haskell这样的语言中已经被certificate是一个可行的实践,而且由于C ++已经实现了“自动”,所以实现返回types演绎只是更进一步。 这种推导应该在专业化时进行,而不是模板定义,因为需要提供给模板的实际types的信息。 声明和定义的分离不再是通用C ++中的常见做法,因为模板体必须写在头文件中,因此模板体几乎总是与模板声明一起使用。 在有多个返回语句且其types不匹配的情况下,编译器可以愉快地报告错误。 总之,如果委员会想要的话,返回types演绎在C ++中是完全可能的。 而且这非常重要,因为手工写入返回types的重复会阻碍普遍使用小型通用帮助函数,这在function和generics编程中是很常见的做法。
包括Ocaml和Felix在内的许多编程语言都可以推导出函数的返回types,而不需要指定它。 在Ocaml中,您可以并且必须在界面中指定它。 在Felix中,我发现一些函数是明智的,在库代码中指定它使它更易于使用。
我很惊讶“汽车”不适用于返回types,这当然是在盘子上。 这被认为太难实施? [这是不平凡的,因为一个函数可以recursion]。
哦,现在我明白了。 问题是愚蠢的模板devisefunction“依赖名称”:
template<class T> auto f(T a) { return af(); }
因此,虽然计算普通函数的返回types由于重载而有点棘手,但由于依赖名称查找,模板是不可能的:只能在实例化之后才能完成。 但是在这之前必须发生超载。 所以自动返回types不能在语言中,因为它不推广到模板。
你的返回types是否经常改变? 为什么你不能明确地指定它? 例:
template<class T> int f(T&& x) { ... }
人们做了二十多年