C ++ std :: tuple的销毁顺序

是否有一个规则说明了一个std ::元组的成员被销毁的顺序?

例如,如果Function1返回一个std::tuple<std::unique_ptr<ClassA>, std::unique_ptr<ClassB>> ,那么我可以确定(当Function2的范围剩下时) ClassB的实例被引用由第二个成员在第一个成员所指的ClassA实例之前被销毁?

 std::tuple< std::unique_ptr< ClassA >, std::unique_ptr< ClassB > > Function1() { std::tuple< std::unique_ptr< ClassA >, std::unique_ptr< ClassB > > garbage; get<0>(garbage).reset( /* ... */ ); get<1>(garbage).reset( /* ... */ ); return garbage; } void Function2() { auto to_be_destroyed = Function1(); // ... do something else // to_be_destroyed leaves scope // Is the instance of ClassB destroyed before the instance of ClassA? } 

标准没有指定std::tuple的销毁顺序。 §20.4.1/ p1规定:

具有两个参数的元组的实例类似于具有相同的两个参数的实例化对。

类似这里不被解释为相同的 ,因此不暗示std::tuple应该有其参数的反向破坏顺序。

鉴于std::tuple的recursion性质,最可能的是,销毁的顺序与其参数的顺序一致。

我也是基于我对GCC BUG 66699的错误报告的假设,在上面的讨论中,我的假设是合理的。

也就是说, std::tuple的销毁顺序是未指定的。

我会提供一个我学到的生活经验教训,而不是直接回答你的问题:

如果你能为多种select制定一个合理的论点,说明为什么这个select应该是标准所要求的select – 那么你不应该假设其中任何一个都是强制的(即使其中一个恰好是)。

在元组的上下文中,请善待维护你的代码的人,不要让元组的销毁顺序可能破坏其他元素的销毁。 这只是邪恶的想象一下,这个倒霉的程序员需要debugging这个东西。 事实上,那些可怜的灵魂可能会在几年后变成你自己,当时你已经忘掉了你今天聪明的把戏。

如果你绝对必须依赖销毁顺序,也许你应该使用一个适当的类,元组的元素作为它的数据成员(你可以写一个析构函数,明确按照什么顺序需要发生什么)或者其他的安排便于更明确地控制破坏。

使用Clang 3.4我得到了std::pair和2元素std::tuple和g ++ 5.3相同的销毁顺序。我得到相反的顺序,这可能主要是由于libstd ++中std::tuple的recursion实现。

所以,基本上归结到我在评论中所说的,是实现定义的。

从BUG报告:

Martin Sebor评论

由于std :: pair成员的布局是完全指定的,所以它们的初始化和销毁​​顺序也是如此。 testing用例的输出反映了这个顺序。

std:stuple子对象的初始化(和销毁)的顺序不太清楚。 至less从阅读规范来看,如果需要特定的顺序,这一点并不明显。

之所以std :: tuple与libstdc ++的输出是std :: pair的反转,是因为依赖于recursioninheritance的实现以相反的顺序存储和构造元组元素:即基类,它存储最后一个元素首先被存储和构造,然后是每个派生类(每个派生类都存储最后的第N个元素)。

错误记者引用的标准[第20.4.1节]的引用

1本小节描述了提供元组types作为类模板元组的元组库,它可以用任意数量的参数实例化。 每个模板参数指定元组中元素的types。 因此,元组是异构的,固定大小的值集合。 具有两个参数的元组的实例类似于具有相同的两个参数的实例化对 。 见20.3。

对这个链接错误的反驳是:

被描述为相似并不意味着它们在每个细节上都是相同的。 std :: pair和std :: tuple是不同的类,每个类都有不同的需求。 如果你认为在这方面需要performance出相同的行为(例如,让他们的子对象按相同的顺序定义),你需要指出保证它的具体措词。