题
我想把我的头周围元组(由@litb),而公共的建议,为它们的使用是对功能返回>1的数值。
这是我通常会利用一个结构,我也不能理解的优势来组在这种情况下-这似乎是一个容易出错的方法,对于身患绝懒惰。
借款的一个例子, 我会用这个
struct divide_result {
int quotient;
int remainder;
};
使用tuple,你会有
typedef boost::tuple<int, int> divide_result;
但没有读码的功能你打电话(或评论,如果你是蠢到相信他们)你有没有想法int商,反之亦然。它似乎不像...
struct divide_result {
int results[2]; // 0 is quotient, 1 is remainder, I think
};
...不会我充满信心。
那么,是什么 都 优势的组在结构补偿的模糊性?
解决方案
元组
我认为我同意你的观点,即什么位置的问题对应于什么变量会引起混乱。但我认为有两个方面。一个是呼叫方,另一个是被叫方:
int remainder;
int quotient;
tie(quotient, remainder) = div(10, 3);
我认为我们得到的结果非常清楚,但如果你必须一次返回更多的值,它会变得混乱。一旦调用者的程序员查找了div
的文档,他就会知道什么位置是什么,并且可以编写有效的代码。根据经验,我会说不会一次返回超过4个值。对于任何事情,更喜欢结构。
输出参数
当然也可以使用输出参数:
int remainder;
int quotient;
div(10, 3, "ient, &remainder);
现在我认为这说明了元组如何比输出参数更好。我们将operator>>
的输入与其输出混合在一起,但没有获得任何优势。更糟糕的是,我们让该代码的读者对tie
的实际返回值有什么疑问。当输出参数有用时,是很好的例子。在我看来,只有当你没有其他方法时才应该使用它们,因为返回值已经被采用并且不能更改为元组或结构。 <=>是使用输出参数的一个很好的示例,因为返回值已经为流保留,因此您可以链接<=>调用。如果您不关心运算符,并且上下文不清楚,我建议您使用指针,在调用方面发信号通知对象实际上用作输出参数,以及适当的注释。 / p>
返回结构
第三种选择是使用结构:
div_result d = div(10, 3);
我认为肯定会因清晰度而获奖。但请注意,您仍然要在该结构中访问结果,结果不是<!> quot; lay bare <!> quot;在表上,就像输出参数和与<=>一起使用的元组的情况一样。
我认为现在最重要的一点是尽可能使一切尽可能通用。所以,假设你有一个可以打印出元组的函数。你可以做到
cout << div(10, 3);
显示结果。我认为,另一方面,元组明显地赢得了多才多艺的性质。使用div_result执行此操作时,需要重载operator <!> lt; <!> lt;,或者需要单独输出每个成员。
其他提示
另一个选择是使用Boost Fusion地图(代码未经测试):
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 ();
。结构不是那么容易的。其次,在使用模板代码时,有时更容易依赖对,而不是为结构类型添加另一个typedef。
如果类型不同,那么一对/元组实际上并不比结构差。想想例如pair<int, bool> readFromFile()
,其中int是读取的字节数,bool是eof是否被命中。在这种情况下添加一个结构对我来说似乎有些过分,特别是因为这里没有歧义。
组是非常有用的语言,如ML或Haskell.
在C++、他们的语法使得它们不那么优雅,但可能是有用的,在下列情况:
你有一个功能,必须返回多个参数,但结果是"当地"的呼叫者和被叫方;你不想定义的结构,只是为这个
你可以使用的领带功能要做一个非常有限形式的模式匹配"a la毫升",这是更加优雅于使用的结构为同一目的。
他们来预先定义的 < 运营商,这可以节省时间。
我倾向于将元组与typedef结合使用,以至少部分缓解“无名元组”问题。例如,如果我有一个网格结构,那么:
//row is element 0 column is element 1
typedef boost::tuple<int,int> grid_index;
然后我使用命名类型:
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
等效或构造函数,然后将此结构用作输入参数,否则首先必须创建一个临时对象:
void foo (A const & a)
{
// ...
}
void bar ()
{
A dummy = { 1, 2 };
foo (dummy);
}
但是,并不是太糟糕,假设维护在我们的结构中添加了一个新成员,无论出于何种原因:
struct A
{
int a;
int b;
int c;
};
聚合初始化的规则实际上意味着我们的代码将继续编译而不会发生变化。因此,我们必须在没有编译器帮助的情况下搜索此结构的所有用法并进行更新。
将其与元组进行对比:
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
}
编译器无法初始化<!>“; Tuple <!>”;使用<=>的结果,因此生成错误,允许您为第三个参数指定正确的值。
最后,元组的另一个优点是它们允许您编写迭代每个值的代码。使用结构是不可能的。
void incrementValues (boost::tuples::null_type) {}
template <typename Tuple_>
void incrementValues (Tuple_ & tuple) {
// ...
++tuple.get_head ();
incrementValues (tuple.get_tail ());
}
防止您的代码被许多结构定义所困扰。对于编写代码的人来说,以及当你只是记录元组中每个元素的内容时,更容易使用它,而不是编写自己的结构/让人们查找结构定义。
元组将更容易编写 - 无需为返回某些内容的每个函数创建新的结构。关于将去哪里的功能文档的文档,无论如何都需要。要使用该功能,我们需要在任何情况下阅读功能文档,并在那里解释元组。
我同意你100%罗迪。
要从方法返回多个值,除了元组之外,您还有几个选项,其中一个最好取决于您的情况:
-
创建新结构。当您返回的多个值相关时,这很好,并且适合创建新的抽象。例如,我认为<!> quot; divide_result <!> quot;通过这个实体是一个很好的通用抽象,使你的代码更清晰,而不仅仅是传递一个无名的元组。然后,您可以创建对此新类型进行操作的方法,将其转换为其他数字类型等。
-
使用<!>“; Out <!>”;参数。通过引用传递多个参数,并通过分配每个out参数返回多个值。当您的方法返回多个不相关的信息时,这是合适的。在这种情况下创建一个新的结构将是过度的,使用Out参数,你强调这一点,加上每个项目都得到它应得的名称。
醇>
元组是邪恶的。