为什么在C++中使用元组不常见?

Zif*_*fre 120 c++ tuples

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

元组允许你做这样的各种酷事:

tie(a,b) = make_tuple(b,a); //swap a and b
Run Code Online (Sandbox Code Playgroud)

这肯定比这更好:

temp=a;
a=b;
b=temp;
Run Code Online (Sandbox Code Playgroud)

当然你总能做到这一点:

swap(a,b);
Run Code Online (Sandbox Code Playgroud)

但是如果要旋转三个值怎么办?你可以用元组做到这一点:

tie(a,b,c) = make_tuple(b,c,a);
Run Code Online (Sandbox Code Playgroud)

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

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

Tre*_*son 122

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

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

换句话说,许多C++程序员只是使用C++编译器的C程序员,也许std::vectorstd::list.这就是使用boost::tuple不常见的一个原因.

  • 有意获得(目前)3次投票但没有评论. (40认同)
  • -1来自我,因为C++程序员并不像这个答案让他们听起来那么愚蠢. (16认同)
  • @Michael Nonsense评论.一旦你拥有图灵完整的语言子集,编程中就没有*需要*.缺乏使用当然并不意味着每个人都理解更高级别的C++结构并选择不使用它们. (9认同)
  • @Mehrdad看了很多C++代码,无论是商业代码还是非商业代码,阅读大量的C++资料,我认为很可能说很大一部分"C++"开发人员只是C开发人员,他们无法获得纯粹的C编译器.例如,模板几乎完全缺少大多数材料(我已经学会了很多爱).奇怪的宏hacks很常见,命名空间严重不足. (5认同)
  • 废话回答.如果确实需要他们,他们会赶上他们.他们不需要; 所以他们没有被使用.说它们没有被使用因为它们不容易被理解是不好的. (5认同)
  • Tbh我永远不需要在可变参数模板元编程之外使用std :: tuple.生活中没有任何意义,我坐在一张悲伤的脸上,想着"如果我只有那些元组".事实上,当我看到元组时,我想"有人会理所当然地需要他们(我不会认为自己是健全的)".元编程之外的人似乎将它们用作"匿名结构",它是如此丑陋并且表明他们头脑中缺乏代码质量和可维护性. (4认同)

Ala*_*met 40

因为它尚未标准化.任何非标准的都有更高的障碍.由于程序员们吵着要他们,所以Boost的作品变得很受欢迎.(hash_map跳跃于脑海).但是,虽然元组很方便,但人们对它的困扰并不是那么压倒性和明确的胜利.

  • 它现在是C++ 11标准的一部分:http://en.cppreference.com/w/cpp/utility/tuple (20认同)

use*_*714 23

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

考虑:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;
Run Code Online (Sandbox Code Playgroud)

因此,如果你想大量使用元组,你可以在任何地方获得元组类型的定义,或者你到处都会看到烦人的长类型名称.我喜欢元组.我在必要时使用它们.但它通常仅限于几种情况,例如N元素索引或使用多重映射来绑定范围迭代器对.它通常在非常有限的范围内.

与Haskell或Python相比,它看起来非常丑陋和hacky.当C++ 0x到达此处并且我们得到'auto'关键字时,元组将开始变得更具吸引力.

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

  • 只有我吗?我不认为保存输入"boost ::"的7个字符是他所指的,而是*其他33个字符*.这是很多类名输入的问题,特别是如果那些也是名称空间作用域.把boost :: tuple <std :: string,std :: set <std :: string>,std :: vector <My :: Scoped :: LongishTypeName >>作为一个荒谬的例子. (19认同)
  • @Zifre:问题是你不应该在头文件中"使用命名空间X",因为它会强制命名空间污染并破坏命名空间. (2认同)

ojr*_*rac 9

对我来说,这是习惯,举手说明:元组并没有为我解决任何新问题,只有少数我已经可以处理好了.交换价值仍然感觉更轻松的老式方法 - 而且,更重要的是,我真的不想想怎么换"更好".它现在已经足够好了.

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

  • "我真的不考虑如何更好地交换." - 当我编写代码时,我会写错误.降低代码复杂性可减少我编写的错误数量.我讨厌一次又一次地创建相同的错误.**是的,我确实考虑过如何<strike> swap </>代码更好**.移动部件较少(LOC,临时变量,错误标识符),代码更易读; 好的代码. (4认同)

Ste*_*sop 8

但是如果要旋转三个值怎么办?

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

好的,所以使用4个等值,最终n元组变得比n-1互换的代码少.如果您自己实现了一个三周期模板,那么使用默认交换这将完成6个分配,而不是您所拥有的4个分配,尽管我希望编译器能够解决简单类型的问题.

您可以提出交换不实用或不合适的情况,例如:

tie(a,b,c) = make_tuple(b*c,a*c,a*b);
Run Code Online (Sandbox Code Playgroud)

打开包装有点尴尬.

但是,有一些已知的方法可以处理元组有用的最常见情况,因此没有采用元组的紧迫性.如果没有别的,我不相信:

tie(a,b,c) = make_tuple(b,c,a);
Run Code Online (Sandbox Code Playgroud)

不做6份,使它完全不适合某些类型(集合最明显).随意说服我,元组是"大"类型的好主意,说这不是这样的:-)

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

  • 一个平局(在技术上是一个层)是一个由非const引用组成的元组.我找不到提升文档,它说明了什么保证operator =和tie/tuple的复制构造函数,当涉及的一些引用具有相同的referand时.但这就是你需要知道的.操作员的天真实现=显然可能会出错...... (2认同)

igo*_*ord 7

正如许多人所指出的那样,元组并不像其他功能那样有用.

  1. 交换和旋转的噱头只是噱头.对于那些之前没有看过它们的人来说,它们完全令人困惑,因为它几乎是每个人,这些噱头只是糟糕的软件工程实践.

  2. 使用元组返回多个值远不如自我文档那么替代 - 返回命名类型或使用命名引用.如果没有这种自我记录,很容易混淆返回值的顺序,如果它们是可相互转换的,并且不是更明智的.


Bri*_*eal 6

不是每个人都可以使用增强,而TR1还没有广泛使用.

  • 很多人使用Boost.那些人也可以使用元组. (3认同)
  • 你问为什么人们不使用它们,我给了一个答案. (3认同)
  • 对于选民来说:我碰巧在政治上不可能使用boost的地方工作,即使在这个日期,我们使用的编译器工具链(对于嵌入式系统)也没有TR1/C++ 11支持. (2认同)

Dan*_*anM 5

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


小智 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...
}
Run Code Online (Sandbox Code Playgroud)

这并不漂亮,但看看替代方案有多丑陋。请注意,我仍然需要一个状态值,但代码不再具有可读性,也不会更短。它也可能更慢,因为我承担了 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 ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • 使用 `tuple` 使代码 _less_ 可读,而不是更多。现在的大多数代码都包含大量符号 - 看到 `std::tuple` 就可以清楚地看到它到底是什么。 (2认同)