C ++ 11中元组有哪些好的用例?

时间:2012-04-21 13:31:56

标签: c++ c++11 tuples

在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

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

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

最后两点由tie函数解决。鉴于这些缺点,元组的用例是什么?

UPDATE 结果证明VS2010 SP1调试器无法显示以下数组std::tuple<int, int, int> stack[40]的内容,但在使用结构编码时它可以正常工作。所以这个决定基本上是一个明智的选择:如果你必须检查它的值,使用结构[特别是像GDB这样的调试器很重要。

9 个答案:

答案 0 :(得分:57)

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

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

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

int a;
int b;
std::tie(a,b)=fun();

答案 1 :(得分:26)

嗯,imho,最重要的部分是通用代码。编写适用于各种结构的通用代码要比编写处理元组的泛型要困难得多。例如,你自己提到的std::tie函数几乎不可能用于结构。

这允许你做这样的事情:

  • 存储延迟执行的函数参数(例如this question
  • 使用std::tie
  • 返回多个参数,而无需繁琐(un)打包
  • 合并(非等同类型)数据集(例如来自并行执行),可以像std::tuple_cat一样简单地完成。

问题是,它不会因这些用途而停止,人们可以在此列表上进行扩展,并根据结构更难处理的元组编写通用功能。谁知道,也许明天有人为序列化目的找到了很好的用途。

答案 2 :(得分:20)

我认为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中节省了很多精力。

答案 3 :(得分:13)

你曾经使用过std::pair吗?您使用std::tuple的许多地方都很相似,但并不仅限于两个值。

你为元组列出的缺点也适用于std :: pair,有时你想要一个比firstsecond更具表现力的类型,其成员的名字更好,但有时候你不会这样做。需要那个。这同样适用于元组。

答案 4 :(得分:10)

我认为除了某些通用库功能的实现细节之外,元组没有很好用。

输入中的(可能)保存不会抵消结果代码的自我记录属性中的损失。

将元组替换为仅占用字段的有意义名称的结构,将字段名称替换为“数字”(就像std :: pair的错误概念一样)。

使用元组返回多个值远不如自我文档,而是替代方法 - 返回命名类型或使用命名引用。如果没有这种自我记录,如果它们是可相互转换的,很容易混淆返回值的顺序。

答案 5 :(得分:6)

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

例如,您不应该使用std :: tuple从具有固定数量的通用输入的普通函数或模板中获得“多次返回”。使用真实的结构。一个真实的对象比std :: tuple cookie-cutter更加“通用”,因为你可以在任何界面上给出一个真正的对象。它还将为您提供更多类型安全性和公共图书馆的灵活性。

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

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

GeoCoordinate  GetLocation() const;

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

答案 6 :(得分:1)

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

答案 7 :(得分:1)

我无法评论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);
}

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

答案 8 :(得分:0)

F.21:要返回多个“输出”值,最好返回结构或元组

最好使用命名结构,该结构具有返回值的语义。否则,无名元组在通用代码中很有用。

例如,如果返回的值是输入流中的值和错误代码,则这些值不会相距太远。它们之间的相关性不足以证明拥有两者的专用结构是合理的。与此不同,x和y对宁愿具有类似Point的结构。

source I reference由Herb Sutter的Bjarne Stroustrup维护,因此我认为值得信赖。