考虑以下因素:
std::string make_what_string( const std::string &id );
struct basic_foo
{
basic_foo( std::string message, std::string id );
};
struct foo
: public basic_foo
{
foo::foo( std::string id)
: basic_foo( make_what_string( id ), std::move( id ) ) // Is this valid?
{
}
};
因为C ++中的参数评估顺序未指定,所以我想知道是否 这条线
basic_foo( make_what_string( id ), std::move( id ) )
上面代码中的是有效的。
我知道std::move
只不过是一个演员,但什么时候是std :: string
移动ctor执行?在评估了所有参数之后,是时候打电话了
基础构造函数?或者这是在评估参数期间完成的?在
换句话说:
编译器是否这样做:
std::string &&tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);
这是有效的。或者这个:
std::string tmp2 = std::move(id);
std::string tmp1 = make_what_string(id);
basic_foo(tmp1, tmp2);
无效。请注意,在这两种情况下,订单都是“意外” 之一。
答案 0 :(得分:17)
见1.9节:
除非另有说明,否则对单个操作符的操作数和单个表达式的子表达式的评估是不合理的。
和
当调用函数时(无论函数是否为内联函数),在执行每个表达式或语句之前,对与任何参数表达式或指定被调用函数的后缀表达式相关联的每个值计算和副作用进行排序。被调用函数的主体。 [注意:与不同参数表达式相关的值计算和副作用未被排序。 - 结束记录]
我认为问题在于,参数的初始化是否被认为是与参数表达式相关的副作用还不是很清楚。但是,它似乎得到了第5.2.2节的支持:
每个参数的初始化和销毁都发生在上下文中 调用函数。
同一段落中还有一个注释使其更清晰:
调用函数时,应使用相应的参数初始化每个参数(8.3.5)(8.5,12.8,12.1)。 [注意:此类初始化相对于彼此不确定地排序(1.9) - 结束注释]
所以是的,参数的初始化是相对于彼此不确定地排序的。初始化可能发生在以下任何一个订单中:
std::string message = make_what_string(id);
std::string id = std::move( id );
std::string id = std::move( id );
std::string message = make_what_string(id);
在第二种情况下,make_what_string
最终使用移动的字符串。
所以,即使std::move
实际上没有移动任何东西,重要的是实际的移动对于另一个参数也没有排序。
basic_string(basic_string&& str)
的移动构造函数的定义:
[...]
str
处于有效状态且未指定值。
因此,您没有未定义的行为,您有未指定的行为。
答案 1 :(得分:7)
这不是真的有效。函数参数评估的顺序未指定。换句话说,您不知道编译器是否会选择此序列:
tmp1 = make_what_string(id);
tmp2 = std::move(id);
basic_foo(tmp1, tmp2);
或者这个:
tmp1 = std::move(id);
tmp2 = make_what_string(id); //id has already been moved from!
basic_foo(tmp2, tmp1);