Boost :: Tuples vs Structs返回值
我试图让我的头绕着元组(谢谢@litb),而他们使用的常见build议是函数返回> 1值。
这是我通常使用一个结构的东西,我不明白在这种情况下元组的优点 – 这似乎是一个容易出错的方法为最后懒惰。
借用一个例子 ,我会用这个
struct divide_result { int quotient; int remainder; };
使用元组,你会有
typedef boost::tuple<int, int> divide_result;
但是如果没有阅读你所调用函数的代码(或者注释,如果你足够愚蠢的信任它们),你不知道哪个int是商,反之亦然。 这似乎相当…
struct divide_result { int results[2]; // 0 is quotient, 1 is remainder, I think };
…这不会使我充满信心。
那么,元组对结构的优点是什么,弥补了模糊性?
元组
我想我同意你说,什么位置的问题对应什么variables会引起混淆。 但我认为有两面。 一个是呼叫方 ,另一个是被 呼叫 方 :
int remainder; int quotient; tie(quotient, remainder) = div(10, 3);
我认为我们得到的东西非常清晰,但是如果你必须一次返回更多的价值,那么它会变得令人困惑。 一旦调用者的程序员查阅了div
的文档,他就会知道什么位置是什么,并能写出有效的代码。 作为一个经验法则,我会说不要一次返回超过4个值。 对于任何超越,喜欢一个结构。
输出参数
当然也可以使用输出参数:
int remainder; int quotient; div(10, 3, "ient, &remainder);
现在我认为这说明了元组如何比输出参数更好。 我们混合了div
的input和输出,而没有获得任何好处。 更糟糕的是,我们让那些代码的读者怀疑div
的实际返回值是什么。 输出参数很有用的时候有很好的例子。 在我看来,只有当你没有别的办法的时候,你才应该使用它们,因为返回值已经被使用了,不能改变成元组或结构。 operator>>
就是一个很好的例子,你可以在这里使用输出参数,因为返回值已经为stream保留了,所以你可以链接operator>>
calls。 如果你不需要操作符,上下文也不清楚,我build议你使用指针,在调用方发出信号,告诉对象实际上是用作输出参数的,除了适当的注释。
返回一个结构
第三个选项是使用一个结构体:
div_result d = div(10, 3);
我认为,这绝对是赢得了奖项。 但是请注意,您仍然可以访问该结构中的结果,并且结果不会在表格中“裸露”,因为输出参数和tie
使用的元组就是这种情况。
我认为现在的一个重点是尽可能使一切尽可能通用。 所以,说你有一个可以打印出元组的函数。 你可以做
cout << div(10, 3);
并显示您的结果。 我认为另一方面,元组明显地赢得了他们的多面性 。 这样做与div_result,你需要重载operator <<,或需要分别输出每个成员。
另一种select是使用Boost Fusion映射(代码未经testing):
struct quotient; struct remainder; using boost::fusion::map; using boost::fusion::pair; typedef map< pair< quotient, int >, pair< remainder, int > > div_result;
您可以直观地访问结果:
using boost::fusion::at_key; res = div(x, y); int q = at_key<quotient>(res); int r = at_key<remainder>(res);
还有其他的一些优点,比如遍历地图的字段等。查看doco了解更多信息。
使用元组,可以使用tie
,这有时是相当有用的: std::tr1::tie (quotient, remainder) = do_division ();
。 结构不是那么容易。 其次,在使用模板代码的时候,有时候更容易依赖对,而不是为结构types添加另一个typedef。
如果types不同,那么pair / tuple实际上并不比结构更差。 想一下例如pair<int, bool> readFromFile()
,其中int是读取的字节数,bool是eof是否被命中。 在这种情况下添加一个结构对我来说似乎是矫枉过正的,特别是在这里没有任何含糊之处。
元组在ML或Haskell等语言中非常有用。
在C ++中,它们的语法使得它们不那么优雅,但在以下情况下可能会有用:
-
你有一个函数,必须返回多个参数,但结果是“本地”的来电和被调用者; 你不想为此定义一个结构
-
你可以使用tiefunction来做一个非常有限的模式匹配forms“a la ML”,比起为了同样的目的而使用结构更优雅。
-
他们来预定义<运营商,这可以节省时间。
我倾向于使用元组和typedefs至less部分地减轻“无名元组”的问题。 例如,如果我有一个网格结构,那么:
//row is element 0 column is element 1 typedef boost::tuple<int,int> grid_index;
然后我使用命名的types为:
grid_index find(const grid& g, int value);
这是一个有点人为的例子,但我想大多数时候它在可读性,清晰性和易用性之间达到了一个愉快的媒介。
或者在你的例子中:
//quotient is element 0 remainder is element 1 typedef boost:tuple<int,int> div_result; div_result div(int dividend,int divisor);
你没有的结构元组的一个特征是初始化。 考虑下面的内容:
struct A { int a; int b; };
除非你写了一个make_tuple
等价物或构造函数,然后使用这个结构作为input参数,你首先必须创build一个临时对象:
void foo (A const & a) { // ... } void bar () { A dummy = { 1, 2 }; foo (dummy); }
但是,不要太糟糕,维护会为我们的结构添加一个新成员,无论出于何种原因:
struct A { int a; int b; int c; };
聚合初始化的规则实际上意味着我们的代码将继续编译而不会改变。 因此,我们不得不search这个结构的所有用法并更新它们,而不需要编译器的帮助。
与元组对比一下:
typedef boost::tuple<int, int, int> Tuple; enum { A , B , C }; void foo (Tuple const & p) { } void bar () { foo (boost::make_tuple (1, 2)); // Compile error }
编译器不能通过make_tuple初始化“元组”,因此会生成错误,使您可以为第三个参数指定正确的值。
最后,元组的另一个优点是它允许你编写迭代每个值的代码。 这是不可能的使用结构。
void incrementValues (boost::tuples::null_type) {} template <typename Tuple_> void incrementValues (Tuple_ & tuple) { // ... ++tuple.get_head (); incrementValues (tuple.get_tail ()); }
防止你的代码散布许多结构体定义。 对于编写代码的人来说,更容易,而当你仅仅logging元组中的每个元素时,就更容易使用它,而不是写自己的结构/让人们查找结构定义。
元组将更容易编写 – 不需要为每个返回值的函数创build一个新的结构体。 关于什么地方将会去哪些function文档的文档,无论如何将是需要的。 要使用这个函数,在任何情况下都需要读取函数文档,这里将解释元组。
我同意你100%罗迪。
要从一个方法中返回多个值,除了元组之外,还有几个选项取决于你的情况:
-
创build一个新的结构。 当你要返回的多个值相关时 ,这是很好的,并且创build一个新的抽象是合适的。 例如,我认为“divide_result”是一个很好的通用抽象,通过传递这个实体使得你的代码比传递一个无名的元组更清晰。 然后,您可以创build对这个新types进行操作的方法,将其转换为其他数字types等。
-
使用“输出”参数。 通过引用传递几个参数,并通过分配给每个out参数返回多个值。 当您的方法返回多个不相关的信息时,这是适当的。 在这种情况下创build一个新的结构将是矫枉过正的,而用Out参数强调这一点,再加上每个项目都会得到它应有的名称。
元组是邪恶的。