c++ 11中的元组有什么好的用例?

时间:2020-12-18 00:33:27

What are good use-cases for using tuples in C++11? For example, I have a function that defines a local struct as follows:

在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

I was considering to replace the SI struct with an std::tuple<int,int,int>, which is a far shorter declaration with convenient constructors and operators already predefined, but with the following disadvantages:

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

  • Tuple elements are hidden in obscure, implementation-defined structs. Even though Visual studio interprets and shows their contents nicely, I still can't put conditional breakpoints that depend on value of tuple elements.
  • 元组元素隐藏在晦涩的、实现定义的结构中。尽管Visual studio很好地解释和显示了它们的内容,但我仍然不能放置依赖于元组元素值的条件断点。
  • Accessing individual tuple fields (get<0>(some_tuple)) is far more verbose than accessing struct elements (s.l).
  • 访问单个tuple字段(get<0>(some_tuple))要比访问struct元素(s.l)冗长得多。
  • Accessing fields by name is far more informative (and shorter!) than by numeric index.
  • 通过名称访问字段比通过数字索引要有用得多(而且更短!)

The last two points are somewhat addressed by the tie function. Given these disadvantages, what would be a good use-case for tuples?

最后两点由tie函数进行了一定程度的处理。考虑到这些缺点,对于元组来说,什么是好的用例呢?

UPDATE Turns out that VS2010 SP1 debugger cannot show the contents of the following array std::tuple<int, int, int> stack[40], but it works fine when it's coded with a struct. So the decision is basically a no-brainer: if you'll ever have to inspect its values, use a struct [esp. important with debuggers like GDB].

更新结果显示,VS2010 SP1调试器不能显示以下数组std:::tuple stack[40]的内容,但是在使用struct进行编码时,它可以正常工作。因此,这个决定基本上是无需动脑筋的:如果你要检查它的值,请使用struct [esp]。对于像GDB这样的调试器来说很重要。 ,>

8 个解决方案

#1


25  

Well, imho, the most important part is generic code. Writing generic code that works on all kinds of structs is a lot harder than writing generics that work on tuples. For example, the std::tie function you mentioned yourself would be very nearly impossible to make for structs.

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

this allows you to do things like this:

这让你可以做这样的事情:

  • Store function parameters for delayed execution (e.g. this question )
  • 存储延迟执行的函数参数(例如,这个问题)
  • Return multiple parameters without cumbersome (un)packing with std::tie
  • 返回多个参数,没有笨重(un)包装与std::tie
  • Combine (not equal-typed) data sets (e.g. from parallel execution), it can be done as simply as std::tuple_cat.
  • 合并(非等类型)数据集(例如,从并行执行),它可以简单地作为std::tuple_cat。

The thing is, it does not stop with these uses, people can expand on this list and write generic functionality based on tuples that is much harder to do with structs. Who knows, maybe tomorrow someone finds a brilliant use for serialization purposes.

问题是,它并没有随着这些用途而停止,人们可以在这个列表中展开,并基于元组编写通用的功能,而元组在使用结构时要困难得多。谁知道呢,也许明天会有人发现串行化的用途。

#2


55  

It is an easy way to return multiple values from a function;

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

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

The result values can be used elegantly as follows:

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

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

#3


18  

I think most use for tuples comes from std::tie:

我认为元组最常用的用法来自于std: tie: tie: tie:

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

Along with many other examples in the answers here. I find this example to be the most commonly useful, however, as it saves a lot of effort from how it used to be in C++03.

这里还有很多其他的例子。不过,我发现这个示例是最常用的,因为它节省了很多工作,不像在c++ 03中那样。

#4


12  

Have you ever used std::pair? Many of the places you'd use std::tuple are similar, but not restricted to exactly two values.

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

The disadvantages you list for tuples also apply to std::pair, sometimes you want a more expressive type with better names for its members than first and second, but sometimes you don't need that. The same applies to tuples.

您列出的tuples的缺点也适用于std::pair,有时您想要一个更有表现力的类型,其成员的名称要比第一和第二的好,但有时您不需要这样做。元组也是如此。

#5


8  

I think there is NO good use for tuples outside of implementation details of some generic library feature.

我认为在一些泛型库特性的实现细节之外的元组没有很好的用途。

The (possible) saving in typing do not offset the losses in self-documenting properties of the resulting code.

输入的(可能的)保存不会抵消生成代码的自记录属性的损失。

Substituting tuples for structs that just takes away a meaningful name for a field, replacing the field name with a "number" (just like the ill-conceived concept of an std::pair).

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

Returning multiple values using tuples is much less self-documenting then the alternatives -- returning named types or using named references. Without this self-documenting, it is easy to confuse the order of the returned values, if they are mutually convertible.

使用元组返回多个值要比使用替代方法返回命名类型或使用命名引用少得多。如果没有这个自文档,如果返回值是可交换的,那么很容易混淆返回值的顺序。

#6


6  

The real use cases are situations where you have unnameable elements- variadic templates and lambda functions. In both situations you can have unnamed elements with unknown types and thus the only way to store them is a struct with unnamed elements: std::tuple. In every other situation you have a known # of name-able elements with known types and can thus use an ordinary struct, which is the superior answer 99% of the time.

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

For example, you should NOT use std::tuple to have "multiple returns" from ordinary functions or templates w/ a fixed number of generic inputs. Use a real structure for that. A real object is FAR more "generic" than the std::tuple cookie-cutter, because you can give a real object literally any interface. It will also give you much more type safety and flexibility in public libraries.

例如,您不应该使用std::tuple来获得普通函数或模板w/固定数量的通用输入的“多重返回”。用一个真实的结构。真正的对象比std:::tuple cookie-cutter要“通用”得多,因为你可以给一个真正的对象任何接口。它也将给你更多的类型安全和灵活性在公共图书馆。

Just compare these 2 class member functions:

比较这两个类成员函数:

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

GeoCoordinate  GetLocation() const;

With a real 'geo coordinate' object I can provide an operator bool() that returns false if the parent object had no location. Via its APIs users could get the x,y,z locations. But here's the big thing- if I decide to make GeoCoordinate 4D by adding a time field in 6 months, current users's code won't break. I cannot do that with the std::tuple version.

使用真正的“geo坐标”对象,我可以提供一个操作符bool(),如果父对象没有位置,则返回false。通过它的api,用户可以获得x,y,z的位置。但有一点很重要——如果我决定在6个月内添加一个时间字段来创建GeoCoordinate 4D,那么当前用户的代码不会崩溃。对于std::tuple版本,我不能这样做。

#7


1  

Interoperation with other programming languages that use tuples, and returning multiple values without having the caller have to understand any extra types. Those are the first two that come to my mind.

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

#8


1  

I cannot comment on mirk's answer, so I'll have to give a separate answer:

我不能评论mirk的回答,所以我必须给出一个单独的回答:

I think tuples were added to the standard also to allow for functional style programming. As an example, while code like

我认为元组被添加到标准中也是为了允许函数式编程。作为一个例子,当代码像

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

is ubiquitous in traditional C++, because it is the only way to have multiple objects returned by a function, this is an abomination for functional programming. Now you may write

在传统的c++中是普遍存在的,因为它是一个函数返回多个对象的唯一方法,这是函数式编程的一个讨厌之处。现在你可以写

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

Thus having the chance to avoid side effects and mutability, to allow for pipelining, and, at the same time, to preserve the semantic strength of your function.

这样就有机会避免副作用和易变性,允许流水线操作,同时保持函数的语义强度。

#1


25  

Well, imho, the most important part is generic code. Writing generic code that works on all kinds of structs is a lot harder than writing generics that work on tuples. For example, the std::tie function you mentioned yourself would be very nearly impossible to make for structs.

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

this allows you to do things like this:

这让你可以做这样的事情:

  • Store function parameters for delayed execution (e.g. this question )
  • 存储延迟执行的函数参数(例如,这个问题)
  • Return multiple parameters without cumbersome (un)packing with std::tie
  • 返回多个参数,没有笨重(un)包装与std::tie
  • Combine (not equal-typed) data sets (e.g. from parallel execution), it can be done as simply as std::tuple_cat.
  • 合并(非等类型)数据集(例如,从并行执行),它可以简单地作为std::tuple_cat。

The thing is, it does not stop with these uses, people can expand on this list and write generic functionality based on tuples that is much harder to do with structs. Who knows, maybe tomorrow someone finds a brilliant use for serialization purposes.

问题是,它并没有随着这些用途而停止,人们可以在这个列表中展开,并基于元组编写通用的功能,而元组在使用结构时要困难得多。谁知道呢,也许明天会有人发现串行化的用途。

#2


55  

It is an easy way to return multiple values from a function;

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

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

The result values can be used elegantly as follows:

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

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

#3


18  

I think most use for tuples comes from std::tie:

我认为元组最常用的用法来自于std: tie: tie: tie:

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

Along with many other examples in the answers here. I find this example to be the most commonly useful, however, as it saves a lot of effort from how it used to be in C++03.

这里还有很多其他的例子。不过,我发现这个示例是最常用的,因为它节省了很多工作,不像在c++ 03中那样。

#4


12  

Have you ever used std::pair? Many of the places you'd use std::tuple are similar, but not restricted to exactly two values.

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

The disadvantages you list for tuples also apply to std::pair, sometimes you want a more expressive type with better names for its members than first and second, but sometimes you don't need that. The same applies to tuples.

您列出的tuples的缺点也适用于std::pair,有时您想要一个更有表现力的类型,其成员的名称要比第一和第二的好,但有时您不需要这样做。元组也是如此。

#5


8  

I think there is NO good use for tuples outside of implementation details of some generic library feature.

我认为在一些泛型库特性的实现细节之外的元组没有很好的用途。

The (possible) saving in typing do not offset the losses in self-documenting properties of the resulting code.

输入的(可能的)保存不会抵消生成代码的自记录属性的损失。

Substituting tuples for structs that just takes away a meaningful name for a field, replacing the field name with a "number" (just like the ill-conceived concept of an std::pair).

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

Returning multiple values using tuples is much less self-documenting then the alternatives -- returning named types or using named references. Without this self-documenting, it is easy to confuse the order of the returned values, if they are mutually convertible.

使用元组返回多个值要比使用替代方法返回命名类型或使用命名引用少得多。如果没有这个自文档,如果返回值是可交换的,那么很容易混淆返回值的顺序。

#6


6  

The real use cases are situations where you have unnameable elements- variadic templates and lambda functions. In both situations you can have unnamed elements with unknown types and thus the only way to store them is a struct with unnamed elements: std::tuple. In every other situation you have a known # of name-able elements with known types and can thus use an ordinary struct, which is the superior answer 99% of the time.

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

For example, you should NOT use std::tuple to have "multiple returns" from ordinary functions or templates w/ a fixed number of generic inputs. Use a real structure for that. A real object is FAR more "generic" than the std::tuple cookie-cutter, because you can give a real object literally any interface. It will also give you much more type safety and flexibility in public libraries.

例如,您不应该使用std::tuple来获得普通函数或模板w/固定数量的通用输入的“多重返回”。用一个真实的结构。真正的对象比std:::tuple cookie-cutter要“通用”得多,因为你可以给一个真正的对象任何接口。它也将给你更多的类型安全和灵活性在公共图书馆。

Just compare these 2 class member functions:

比较这两个类成员函数:

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

GeoCoordinate  GetLocation() const;

With a real 'geo coordinate' object I can provide an operator bool() that returns false if the parent object had no location. Via its APIs users could get the x,y,z locations. But here's the big thing- if I decide to make GeoCoordinate 4D by adding a time field in 6 months, current users's code won't break. I cannot do that with the std::tuple version.

使用真正的“geo坐标”对象,我可以提供一个操作符bool(),如果父对象没有位置,则返回false。通过它的api,用户可以获得x,y,z的位置。但有一点很重要——如果我决定在6个月内添加一个时间字段来创建GeoCoordinate 4D,那么当前用户的代码不会崩溃。对于std::tuple版本,我不能这样做。

#7


1  

Interoperation with other programming languages that use tuples, and returning multiple values without having the caller have to understand any extra types. Those are the first two that come to my mind.

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

#8


1  

I cannot comment on mirk's answer, so I'll have to give a separate answer:

我不能评论mirk的回答,所以我必须给出一个单独的回答:

I think tuples were added to the standard also to allow for functional style programming. As an example, while code like

我认为元组被添加到标准中也是为了允许函数式编程。作为一个例子,当代码像

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

is ubiquitous in traditional C++, because it is the only way to have multiple objects returned by a function, this is an abomination for functional programming. Now you may write

在传统的c++中是普遍存在的,因为它是一个函数返回多个对象的唯一方法,这是函数式编程的一个讨厌之处。现在你可以写

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

Thus having the chance to avoid side effects and mutability, to allow for pipelining, and, at the same time, to preserve the semantic strength of your function.

这样就有机会避免副作用和易变性,允许流水线操作,同时保持函数的语义强度。