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出相同的行为(例如,让他们的子对象按相同的顺序定义),你需要指出保证它的具体措词。