std::pair和std::tuple在只有两个成员时的区别是什么?

132

std::pair 和仅包含两个成员的 std::tuple 之间有什么区别吗?(除了显而易见的 std::pair 需要恰好两个成员,而 tuple 可能有更多或更少成员…)

6个回答

131

有一些不同之处:

  1. std::tuple 不要求标准始终是 标准布局。如果 TY 都是标准布局,则每个 std::pair<T,Y> 都是标准布局。

  2. 获取 pair 的内容比获取 tuple 更容易。在 tuple 的情况下,您必须使用函数调用,而在 pair 的情况下只需使用成员字段。

但仅此而已。


9
“从一对中获取数据比从元组中获取数据稍微容易些。稍微。”我注意到了。:P 尽管 .first.second 很方便,但如果代码变更需要第三个(或更多)成员,则它们并没有提供任何帮助。我已经注意到,在任何 Getter 中,我倾向于始终使用 std::get,这样我就不需要改变所有内容,只需改变数据类型和任何 make_pair 调用为 make_tuple 调用即可。 - Casey
5
我按下“.”,我的IDE会弹出一个成员列表。我觉得人们不需要把这个说得那么明白。 - Nicol Bolas
2
我想知道C++17中的结构化绑定是否已经使得这个答案的第1点和第2点都无效了?如果是这样,请再添加一个C++17版本。 - sandthorn
@sandthorn:使用结构化绑定如何使 tuple 满足标准布局? - Nicol Bolas
我想补充一点,其实两种方法并没有太大的区别,这样做只会让人感到困惑。std::pair 可能比 std::tuple 更容易实现 constexpr。在我看来,现代代码应该使用 std::tuple,除非需要使用 constexpr。 - Tanveer Badar
显示剩余2条评论

51

std::tuple 的名称较长(多了一个字母)。其中更多的字母需要用右手输入,所以对大多数人来说更容易输入。

话虽如此,std::pair 只能有两个值——不是零、一个、三个或更多。只有两个值。相比之下,元组几乎没有关于值数量的语义限制。因此,如果您想要指定一对值,std::pair 是更准确、类型安全的类型。


30
哈哈!很棒,你考虑了输入的方式!但我想指出,我打字时“pair”比“tuple”快20%以上。这是因为我的手交替输入每个字符,即右手边:p,左手边:a,右手边:i,左手边:r。至少对我来说,我觉得这样更容易! - 不过你还是得到了+1分! - Richard Corden
22
因此,如果你想指定一对值,使用std::pair会更准确、类型安全。它只是(可能)更直接地表达了意图,而并非更加类型安全或“准确”。 - ildjarn
1
@Arafangion:std::tuple<>也是类型安全的(它怎么可能不是呢?),而2在语义上与pair没有区别。 - ildjarn
2
因此,std::pair是一种更准确、类型安全的类型,我认为任何英语使用者都会认为'pair'和'two'是完全同义的。:-] - ildjarn
8
我们在进行微小的区分,但是它们并非完全同义。当你说“two shoes”时,你是指“可能是两只左鞋”的“两只鞋”,还是指“一双鞋”(其中一只始终是左脚,另一只始终是右脚)? - Arafangion
显示剩余2条评论

35
这是一个非常晚的回答,但需要注意的是,因为std::pair 是使用成员变量定义的,所以它的大小不能使用空基类优化进行优化(firstsecond必须占用不同的地址,即使其中一个或两个是一个空类也是如此)。由于 second_type 的任何对齐要求都会加剧这种情况,所以在最坏的情况下,结果得到的 std::pair 实际上需要的大小基本上是两倍。

std::tuple 只允许通过辅助函数访问,所以如果其中之一为空,则可以从中派生出它,从而节省开销。至少GCC的实现肯定这样做了...您可以浏览头文件来验证这一点,但这里也是证据。


7
当然,C++20 [[no_unique_address]] 应该消除 std::pair 的不利之处。 - Deduplicator
1
"std::tuple 只允许通过辅助函数进行访问",或者使用 C++17 的结构化绑定。很遗憾,如此多合理的 C++ 答案这些天很快就过时了。 :-( - cosimo193
思考一下,这个答案实际上是错误的。考虑一个空的、平凡可复制的 T。最后一个意味着从另一个现有的 T 中进行 memcpy 是可以的。这将复制一个字节,因为空类型的大小为 1。如果你有一个 tuple<T, int>,如果你得到一个对 T 的引用,你可以复制一个字节到它上面。如果 tuple 优化了 T 的存储,那么它无疑会与 int 重叠。因此,复制该字节部分地复制了 int,从而破坏了它。 - Nicol Bolas
这种情况不会发生在基类中,因为规则中有一个明确的例外,即何时可以对对象进行memcpy:如果对象是基类子对象,则不可以(注意:在C++20中,这个例外扩展到了no_unique_address成员子对象)。但是对于元组的成员,没有这样的例外。 - Nicol Bolas

30

请注意,在C++17中,可以使用相同的接口从具有两个元素的pair和tuple中读取数据。

auto [a, b] = FunctionToReturnPairOrTuple();

无需使用 get<> :)


这很棒,但无法检测引用。非常适合使用std:touple进行参数化单元测试。 - no one special

2

值得注意的是,cppreference网站指出:

“pair是std::tuple的一种特殊情况,它只有两个元素。”


1
回顾[std::pair]的更改历史,当问题被提出时,这段文字并未出现。这段文字在历史记录中最早出现的时间是[两年后]。 - Casey

2

就我个人而言,我发现 std::tuple 在 GDB 输出方面要难以阅读得多。显然,如果您需要的值超过2个,则 std::pair 将无法满足需求,但我认为这是结构体的一个优点。


这就是为什么当我在类中使用它们时,我会将粗略的行std::get<0>(tupleName)包装在getter中; GetX()更容易阅读和更短。它有一个小缺点,如果您忘记将其设置为const方法,则某人可能会做一些愚蠢的事情,例如:GetX() = 20; - Casey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接