C ++ 11-C ++ std :: tuple销毁顺序
是否有规则说明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?
}
针对您的问题,我将提供我所学的人生课程,而不是直接的答案:
如果您可以为多种替代方案制定一个合理的论据,说明为何该替代方案应成为标准规定的替代方案-那么您不应假定其中的任何替代方案都是强制性的(即使其中一种恰好是强制性的)。
在元组的上下文中,请对维护您的代码的人保持友好,不要让元组元素的破坏顺序潜在地破坏其他元素的破坏。 那真是邪恶……想像一下不幸的程序员将需要调试这个东西。 实际上,几年后,当您已经忘记了过去的聪明技巧时,那个可怜的灵魂可能就是您自己。
如果您绝对必须依赖销毁顺序,那么也许您应该只使用一个适当的类,将元组的元素作为其数据成员(可以编写一个析构函数,以明确需要按什么顺序发生什么),或者其他一些方法 安排有助于更明确地控制销毁。
该标准未指定std::tuple
的销毁顺序。§20.4.1/ p1指出以下事实:
具有两个参数的元组实例化类似于 具有相同两个参数的对的实例化。
此处类似的内容不解释为相同,因此不暗示std::tuple
应该具有其参数的反向销毁顺序。
鉴于std::tuple
的递归性质,最可能的是破坏的顺序与其论据的顺序是一致的。
我的假设也基于GCC BUG 66699的错误报告,在讨论中我的上述假设是合理的。
也就是说,未指定std::tuple
的销毁顺序。
使用Clang 3.4,对于std::pair
和2个元素std::tuple
,我得到相同的销毁顺序;对于g ++ 5.3,我得到相反的顺序,这可能主要是由于libstd ++中std::tuple
的递归实现。
因此,它基本上可以归结为我在评论中所说的,它是实现定义的。
从BUG报告中:
Martin Sebor的评论
由于完全指定了std :: pair成员的布局,因此 它们的初始化和销毁顺序。 测试的输出 大小写反映了此顺序。
std:stuple子对象的初始化(和销毁)顺序 不太明确地指定。 至少从以下情况来看不是立即显而易见的 如果需要任何特殊订单,请阅读我的规格说明。
libstdc ++的std :: tuple输出的原因是相反的 std :: pair的原因是因为实现依赖于递归 反向继承,存储和构造元组元素 顺序:即存储最后一个元素的基类 并首先构造,然后是每个派生类(每个类 存储最后-第N个元素)。
错误报告程序引用的标准[第20.4.1节]中的引用
1本节描述了提供元组的元组库 type作为可以用任何实例化的类模板元组 参数数量。 每个模板参数都指定了 元组中的元素。 因此,元组是异构的 固定大小的值集合。 两个元组的实例化 参数类似于具有相同两个的对的实例化 论点。 见20.3。
链接错误中对此的争论是:
被描述为相似并不意味着它们在每个方面都是相同的 详情。 std :: pair和std :: tuple是不同的类 每个要求。 如果您认为要求行为 在这方面完全相同(即,在 同样的顺序),您需要指向特定的措辞 保证它。