45

在 C++11 中使用元组有哪些好的用例?例如,我有一个定义局部结构的函数,如下所示:

template<typename T, typename CmpF, typename LessF>
void mwquicksort(T *pT, int nitem, const int M, CmpF cmp, LessF less)
{
  struct SI
  {
    int l, r, w;
    SI() {}
    SI(int _l, int _r, int _w) : l(_l), r(_r), w(_w) {}
  } stack[40];

  // etc

我正在考虑用 struct 替换SIstruct std::tuple<int,int,int>,这是一个更短的声明,带有已经预定义的方便的构造函数和运算符,但有以下缺点:

  • 元组元素隐藏在模糊的、实现定义的结构中。即使 Visual Studio 很好地解释和显示了它们的内容,我仍然不能放置依赖于元组元素值的条件断点。
  • 访问单个元组字段 ( get<0>(some_tuple)) 比访问结构元素 ( s.l) 详细得多。
  • 按名称访问字段比按数字索引提供更多信息(而且更短!)。

最后两点在某种程度上由tie函数解决。考虑到这些缺点,什么是元组的好用例?

更新结果是 VS2010 SP1 调试器无法显示以下数组的内容std::tuple<int, int, int> stack[40],但是当它使用结构编码时它可以正常工作。所以这个决定基本上是不费吹灰之力的:如果你必须检查它的值,使用 struct [esp. 对于像 GDB 这样的调试器很重要]。

4

9 回答 9

60

从函数返回多个值是一种简单的方法;

std::tuple<int,int> fun();

结果值可以优雅地使用如下:

int a;
int b;
std::tie(a,b)=fun();
于 2012-04-21T13:48:00.430 回答
30

好吧,恕我直言,最重要的部分是通用代码。编写适用于各种结构的泛型代码比编写适用于元组的泛型要困难得多。例如,std::tie您自己提到的函数几乎不可能用于结构。

这使您可以执行以下操作:

  • 存储延迟执行的函数参数(例如这个问题
  • 返回多个参数,无需繁琐(解)打包std::tie
  • 组合(非等类型)数据集(例如来自并行执行),它可以像std::tuple_cat.

问题是,它并不止于这些用途,人们可以扩展这个列表并基于元组编写通用功能,这对于结构来说更难。谁知道呢,也许明天有人会发现用于序列化目的的绝妙用途。

于 2012-04-21T14:03:17.190 回答
20

我认为 s 的大部分用途tuple来自std::tie

bool MyStruct::operator<(MyStruct const &o) const
{
    return std::tie(a, b, c) < std::tie(o.a, o.b, o.c);
}

以及此处答案中的许多其他示例。然而,我发现这个示例是最常用的,因为它比以前在 C++03 中的方式节省了很多精力。

于 2013-04-24T16:06:03.557 回答
13

你用过std::pair吗?您使用的许多地方std::tuple都是相似的,但不限于恰好两个值。

您为元组列出的缺点也适用于 std::pair,有时您想要一种更具表现力的类型,其成员的名称比firstand更好second,但有时您不需要。这同样适用于元组。

于 2012-05-26T16:11:04.803 回答
12

我认为除了一些通用库功能的实现细节之外,元组没有什么用处。

(可能)节省打字并不能抵消结果代码的自文档属性的损失。

将元组替换为仅带走字段的有意义名称的结构,将字段名称替换为“数字”(就像 std::pair 的构思不当的概念一样)。

使用元组返回多个值的自文档化比其他方法要少得多——返回命名类型或使用命名引用。如果没有这种自我记录,如果返回值可以相互转换,则很容易混淆返回值的顺序。

于 2012-12-25T05:56:50.000 回答
9

真正的用例是你有不可命名元素的情况——可变参数模板和 lambda 函数。在这两种情况下,您都可以拥有具有未知类型的未命名元素,因此存储它们的唯一方法是具有未命名元素的结构:std::tuple。在其他所有情况下,您都有已知类型的可命名元素#,因此可以使用普通结构,这是 99% 的最佳答案。

例如,您不应该使用 std::tuple 从具有固定数量的通用输入的普通函数或模板中获得“多次返回”。为此使用真实的结构。真实对象比 std::tuple 千篇一律更“通用”,因为您可以为真实对象提供任何接口。它还将在公共库中为您提供更多的类型安全性和灵活性。

只需比较这两个类成员函数:

std::tuple<double, double, double>  GetLocation() const; // x, y, z

GeoCoordinate  GetLocation() const;

使用真正的“地理坐标”对象,我可以提供一个运算符 bool(),如果父对象没有位置,则返回 false。通过其 API,用户可以获得 x、y、z 位置。但重要的是——如果我决定通过在 6 个月内添加时间字段来制作 GeoCoordinate 4D,则当前用户的代码不会中断。我不能用 std::tuple 版本做到这一点。

于 2014-01-26T01:41:07.083 回答
2

与其他使用元组的编程语言进行互操作,并返回多个值而无需调用者必须理解任何额外的类型。这是我想到的前两个。

于 2012-04-21T13:35:30.970 回答
2

我无法评论 mirk 的答案,所以我必须单独给出一个答案:

我认为元组被添加到标准中也是为了允许函数式编程。例如,虽然代码像

void my_func(const MyClass& input, MyClass& output1, MyClass& output2, MyClass& output3)
{
   // whatever
}

在传统的 C++ 中无处不在,因为它是让一个函数返回多个对象的唯一方法,这对于函数式编程来说是可憎的。现在你可以写

tuple<MyClass, MyClass, MyClass> my_func(const MyClass& input)
{
   // whatever
   return tuple<MyClass, MyClass, MyClass>(output1, output2, output3);
}

因此有机会避免副作用和可变性,允许流水线,同时保持函数的语义强度。

于 2016-06-16T21:43:14.823 回答
0

F.21:要返回多个“out”值,最好返回一个 struct或 tuple

更喜欢在返回值有语义的地方使用命名结构。否则,无名元组在泛型代码中很有用。

例如,如果返回的值是来自输入流的值和错误代码,那么这些值将不会自负。它们的相关性不足以证明有一个专门的结构来容纳两者。不同的是,x 和 y 对宁愿具有类似 Point 的结构。

我参考的来源由 Bjarne Stroustrup, Herb Sutter 维护,所以我认为有点值得信赖。

于 2020-08-11T17:34:37.413 回答