126

为什么似乎没有人在 C++ 中使用元组,无论是Boost 元组库还是 TR1 的标准库?我读过很多 C++ 代码,很少看到元组的使用,但我经常看到很多地方元组可以解决很多问题(通常从函数返回多个值)。

元组允许你做各种很酷的事情,比如:

tie(a,b) = make_tuple(b,a); //swap a and b

这肯定比这更好:

temp=a;
a=b;
b=temp;

当然,你总是可以这样做:

swap(a,b);

但是如果你想旋转三个值呢?你可以用元组做到这一点:

tie(a,b,c) = make_tuple(b,c,a);

元组还使从函数返回多个变量变得更加容易,这可能比交换值更常见。使用返回值的引用当然不是很优雅。

我没有想到的元组有什么大的缺点吗?如果不是,为什么它们很少使用?他们慢吗?还是只是人们不习惯?使用元组是个好主意吗?

4

12 回答 12

125

一个愤世嫉俗的答案是,许多人使用 C++ 编程,但不理解和/或使用更高级别的功能。有时是因为他们不被允许,但许多人根本不尝试(甚至不理解)。

作为一个非增强示例:有多少人使用中的功能<algorithm>

换句话说,许多 C++ 程序员只是使用 C++ 编译器的 C 程序员,也许std::vector还有std::list. 这就是为什么使用boost::tuple不常见的原因之一。

于 2009-05-13T00:00:07.107 回答
43

因为它还不是标准的。任何非标准的东西都有更高的障碍。Boost 的片段已经变得流行,因为程序员们都在叫嚣着要它们。(hash_map 突然想到)。但是,虽然 tuple 很方便,但它并不是人们为它烦恼的压倒性和明显的胜利。

于 2009-05-12T22:10:53.540 回答
23

C++ 元组语法可能比大多数人想要的要冗长得多。

考虑:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

因此,如果你想广泛使用元组,你要么到处得到元组 typedef,要么到处得到令人讨厌的长类型名称。我喜欢元组。我在必要时使用它们。但它通常仅限于几种情况,例如 N 元素索引或使用多重映射来绑定范围迭代器对时。它通常在非常有限的范围内。

与 Haskell 或 Python 之类的东西相比,这一切都非常丑陋和骇人听闻。当 C++0x 出现时,我们得到 'auto' 关键字元组将开始看起来更具吸引力。

元组的有用性与声明、打包和解包它们所需的击键次数成反比。

于 2009-05-12T23:17:40.310 回答
10

对我来说,这是习惯,放下手来:Tuples 并没有为我解决任何新问题,只有几个我已经可以处理得很好。用老式的方式交换值仍然感觉更容易——而且,更重要的是,我并没有真正考虑如何“更好地”交换值。照原样就足够了。

就个人而言,我不认为元组是返回多个值的好方法——听起来像是structs 的工作。

于 2009-05-13T00:26:38.977 回答
8

但是如果你想旋转三个值呢?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

好的,所以有了 4 个 etc 值,最终 n 元组的代码比 n-1 交换少。并且使用默认交换,这会执行 6 个分配,而不是如果您自己实现一个三周期模板,您将拥有 4 个分配,尽管我希望编译器能够为简单类型解决这个问题。

您可以提出互换不实用或不合适的方案,例如:

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

打开包装有点尴尬。

不过,关键是,有一些已知的方法可以处理元组最适合的最常见情况,因此并不迫切需要使用元组。如果不出意外,我不相信:

tie(a,b,c) = make_tuple(b,c,a);

不做 6 份,使其完全不适合某些类型(收藏是最明显的)。随意说服我,元组对于“大”类型是一个好主意,说这不是这样:-)

对于返回多个值,如果值的类型不兼容,则元组是完美的,但如果调用者可能以错误的顺序获取它们,则有些人不喜欢它们。有些人根本不喜欢多个返回值,并且不想通过简化它们来鼓励使用它们。有些人只是更喜欢命名结构的输入和输出参数,并且可能无法用棒球棒说服使用元组。没有考虑味道。

于 2009-05-13T00:16:47.343 回答
8

正如许多人指出的那样,元组并没有其他功能那么有用。

  1. 交换和旋转的噱头只是噱头。对于那些以前没有见过它们的人来说,它们完全是一头雾水,而且由于几乎是每个人,这些噱头只是糟糕的软件工程实践。

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

于 2012-12-25T05:45:46.030 回答
6

不是每个人都可以使用 boost,TR1 还没有广泛使用。

于 2009-05-12T23:44:15.320 回答
5

你很少看到它们,因为设计良好的代码通常不需要它们——在野外,使用匿名结构优于使用命名结构的情况并不多。由于所有元组真正代表的是一个匿名结构,因此大多数编码人员在大多数情况下只使用真实的东西。

假设我们有一个函数“f”,其中一个元组返回可能是有意义的。作为一般规则,此类功能通常足够复杂,以至于它们可能会失败。

如果 "f" CAN 失败,您需要返回状态 - 毕竟,您不希望调用者必须检查每个参数以检测失败。"f" 可能符合以下模式:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

这并不漂亮,但看看替代方案有多丑陋。请注意,我仍然需要一个状态值,但代码不再具有可读性且不短。它也可能更慢,因为我用元组承担了 1 个副本的成本。

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

另一个显着的缺点隐藏在这里 - 使用“ReturnInts”,我可以通过修改“ReturnInts”而不改变“f”的接口来添加改变“f”的回报。元组解决方案不提供该关键功能,这使其成为任何库代码的劣等答案。

于 2012-12-30T02:10:41.217 回答
5

在嵌入式系统上使用 C++ 时,引入 Boost 库变得复杂。它们相互耦合,因此库大小增加。您返回数据结构或使用参数传递而不是元组。在 Python 中返回元组时,数据结构按照返回值的顺序和类型排列,只是不明确。

于 2009-05-13T00:36:34.733 回答
3

当然,元组是有用的,但如前所述,有一些开销和一两个障碍,你必须跳过才能真正使用它们。

如果您的程序始终找到需要返回多个值或交换多个值的地方,那么走元组路线可能是值得的,但有时以经典方式做事会更容易。

一般来说,并不是每个人都已经安装了 Boost,我当然不会为了它的元组功能而去下载它和配置我的包含目录来使用它。我想你会发现已经在使用 Boost 的人比非 Boost 用户更有可能在他们的程序中找到元组的使用,而来自其他语言的迁移者(想到 Python)更可能只是因为缺少元组而感到不安在 C++ 中,而不是探索添加元组支持的方法。

于 2009-05-12T23:32:04.250 回答
1

由于数据存储std::tuple具有 astruct和数组的最差特性;所有访问都是基于第 n 个位置的,但不能tuple使用for循环遍历。

因此,如果 中的元素在tuple概念上是数组,我将使用数组,如果元素在概念上不是数组,则结构(具有命名元素)更易于维护。(a.lastnamestd::get<1>(a))更具解释性。

这使得 OP 提到的转换成为元组唯一可行的用例。

于 2017-07-27T08:29:43.833 回答
0

我感觉很多人使用 Boost.Any 和 Boost.Variant(带有一些工程)而不是 Boost.Tuple。

于 2009-05-12T23:04:47.383 回答