2016-08-21 44 views
45

是否有规则说明std :: tuple的成员以何种顺序销毁?C++ std :: tuple的销毁顺序

例如,如果Function1返回std::tuple<std::unique_ptr<ClassA>, std::unique_ptr<ClassB>>Function2,然后可以我确信(当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? 
} 
+2

我猜测它主要取决于如何在标准库中实现'std :: tuple'。 – Arunmu

+3

我找不到指定'std :: tuple'销毁顺序的规范。可能应该提交为未指定。 – 101010

+1

http://stackoverflow.com/a/27663655/576911 –

回答

33

该标准没有规定销毁的顺序std::tuple。该§20.4.1/ P1的事实规定:

两个参数元组的一个实例是类似于用相同的两种参数的 实例对。

类似这里不被解释为相同,因此它不意味着std::tuple应该有它的参数反破坏秩序。

鉴于std::tuple的递归性质,最可能的是破坏顺序与其参数的顺序一致。

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

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

13

随着锵3.4我得到两个std::pair和2元件std::tuple并用克++ 5.3我得到相反的顺序这可能主要是由于在libstd递归执行std::tuple ++相同的破坏顺序。

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

BUG报告:

评论马丁Sebor

由于性病::对成员的布局完全指定,所以是 为了自己的初始化和销毁​​的。测试 案例的输出反映了这个顺序。

std:stuple子对象 的初始化(和破坏)的顺序不太清楚。至少从 我不清楚如果任何特定的顺序是必需的规范。

之所以输出的std ::元组的libstdC++是反向的std :: 对是因为实现,其依赖于递归 继承,存储和构造元组以相反的顺序 元素:即存储最后一个元素的基类被存储为 ,并且首先构造,然后是每个派生类(其中每个 存储最后的第N个元素)。

从标准[20.4.1节],其错误记者引用到

1本节的报价描述了元组库,提供了一个元组类型 作为类模板的元组,可以是用任何 参数实例化。每个模板参数指定元组中的 元素的类型。因此,元组是异构的,固定大小的值集合。 具有两个参数 的元组实例化与参数相同的两个参数 的实例类似。见20.3。针对这在链接错误作出

的说法是:

描述为类似并不意味着它们在每 细节是相同的。 std :: pair和std :: tuple是不同的类,每个类都有不同的 要求。如果您认为在这方面要求相同地表现 (即,让其子对象在 中定义相同的顺序),则需要指向 保证它的具体措词。

+3

你怎么知道'std :: pair'元素的销毁顺序是相反的? – deepmax

+2

类似!= idententical。 – 101010

+0

@deepmax基于std :: pair的libcxx和libstd ++的实现。 – Arunmu

52

我会提供了一个生命的教训我已经学会了,而不是直接回答,在回答您的问题:

如果你可以制定,对多个备选方案,为什么一个合理的说法该替代方案应该是标准强制要求的替代方案 - 那么您不应该假设其中任何都是(即使其中一个碰巧是)。

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

如果你绝对必须依赖销毁顺序,也许你应该使用一个适当的类来处理元组的元素作为它的数据成员(你可以写一个析构函数,明确以什么顺序发生的事情)或其他一些有助于更明确地控制销毁的安排。

+1

这是一个非常重要的课程!在使用C++进行编程时,这个技巧一直在开发人员的头脑中。 – hbobenicio

+0

我不太理解这一点。由于C++ 11字符串需要连续存储。但是有一个合理的论点(在编写C++ 03时使用的论点)不要求这样做。或者,如果你对这个例子提出异议,就选择另一个标准作出判断的电话,这样可以合理地进行。那么我不应该编写依赖于C++ 11或C++ 14收紧的代码吗?或者你的意思是说,如果标准不清楚,那么不要依靠下一位程序员以同样的方式吐毛? –

+0

或者对于一个故意愚蠢的例子,有一个合理的论点:std :: vector应该被称为std :: dynamic_array,而std :: valarray应该被称为std :: vector。但是我认为在std :: vector这个标准的基础上编写代码是非常合理的:我们不能在不假设的情况下使用它。所以我不认为我已经理解了你的建议适用于什么情况;-) –