我有一个C ++程序。这个程序是这样的:
struct MyT {void memfunc(std::unique_ptr<MyT> arg);};
std::unique_ptr<MyT> obj = /* some init */;
obj->memfunc(std::move(obj));
这是保证有效吗,还是我最终可以在nullptr
上调用成员函数?
标准报价适用。
我知道参数的评估顺序是不合理的,但我不记得w.r.t的排序是什么。被调用的函数对象。
答案 0 :(得分:19)
这里引用证明在函数调用之前对调用函数和相关副作用所需的所有评估都进行了排序。
此外,所有其他未经特异性测序的评估都是不确定的顺序
这并没有对相互评估的子表达式施加任何排序限制,它们相互之间保持 unsequenced 。
1.9程序执行§15
除非另有说明, 对各个运算符的操作数和单个表达式的子表达式的评估都是无效的。
[...]
在运算符的结果的值计算之前,对运算符的操作数的值计算进行排序。如果对标量对象的副作用相对于同一标量对象的其他效果或使用相同标量对象的值进行的值计算未被排序,则行为未定义。
当调用函数(无论函数是否为内联函数)时,在执行每个表达式之前,对与任何参数表达式或指定被调用函数的后缀表达式相关联的每个值计算和副作用进行排序或被调用函数体内的陈述 [注意:与不同参数表达式相关的值计算和副作用未被排序。 - 注意事项]
调用函数(包括其他函数调用)中的每个评估(在执行被调用函数之前或之后没有特别排序)在被调用函数的执行方面是不确定的顺序。 9 C ++中的几个上下文导致对函数调用的评估,即使在翻译单元中没有相应的函数调用语法 。 [...]
对被调用函数的执行的排序约束(如上所述)是被评估的函数调用的特征,无论调用函数的表达式的语法如何。
其他相关引用约为std::move
模板typename remove_reference :: type&amp;&amp;移动(T&amp; t)noexcept;
返回:static_cast&lt; typename remove_reference :: type&amp;&amp;&gt;(t)。
std::unique_ptr<T>.operator->()
:
20.7.1.2.4 unique_ptr观察员
指针运算符 - &gt;()const noexcept;
要求:get()!= nullptr。
返回:get()。
memfunc
按值获取其参数,因此我们有3个调用:
a)obj->memfunc
b)std::move(obj)
c)传递参数的移动构造函数
因为b)没有改变任何东西,我们可以忽略它的论点:
a和c是不确定的顺序,所以要么可以在另一个之前
如果首先发生,一切都很好,改变obj
并不重要
如果c首先发生,则使用归零obj
评估a,违反前置条件,因此我们有UB。
总之,它是未定义的行为,因为其中一个允许的订单具有未定义的行为。
8.2.2函数调用[expr.call]
1函数调用是一个post fi x表达式,后跟括号,其中包含可能为空的逗号分隔的 initializer-clauses 列表,它们构成了函数的参数。 [...]
[...]
5 post fi x-expression 在表达式列表中的每个表达式和任何默认参数之前排序。 [...]
[...]
答案 1 :(得分:18)
是,x
的评估可以在评估f
之前,之后或期间进行(他们未经过排序)。< / p>
[注意:后缀表达式和参数表达式的评估都没有排序 相对于彼此。参数表达式评估的所有副作用在函数之前排序 输入(见1.9)。 - 结束记录]
(C ++ 11,§5.2.2/ 8)