以下 std::move 的情况是多余的吗?
Is the following case of std::move superfluous?
下面的std::move
是多余的吗?
std::string member;
obj(std::initializer_list<std::string> p_list)
: member {std::move(join(p_list))}
{}
这是连接函数:
std::string join(string_initializer_list p_list) {
size_t size {};
for (auto const & s : p_list) {
size += s.size();
}
std::string output;
output.reserve(size);
for (auto const & s : p_list) {
output.append(s);
}
return output;
}
鉴于您的 join
功能正常,并且 return 和 std::string
,是的,std::move
是多余的; join
中的 return 已经是一个 r 值。
除此之外,w/o std::move
,复制省略意味着它可以在没有 std::move
的情况下就地构造结果,而使用 std::move
可以强制它生成一个临时的string
,调用移动构造函数初始化member
,然后销毁临时;工作量不大(主要是几个指针副本),但比您需要做的要多。
不,你不需要 std::move
。 std::move
的作用是将任意值转换为右值。您的函数已经 return 是一个右值,因此就将结果绑定到引用而言,强制转换没有任何效果(这就是您要从右值初始化 member
所追求的) .
实际上,主动使用std::move
抑制了拷贝省略,所以是严格的悲观化:
std::string s = join({}); // construct from prvalue, elidable,
// elision mandatory in C++17
std::string s = std::move(join({})); // temporary object must be constructed,
// s is initialized by moving from the
// temporary
在第一种形式中,std::string s = join({});
,复制省略意味着 join
的 returned 对象直接在 s
的位置构造(没有临时对象是构造并复制或移动),而且,函数体中的output
变量也被省略并直接在return值中构造,即在s
中。对于 std::move
,第一个省略步骤不可用。
为清楚起见:任何按值 returns 并因此 returns 右值的函数都不需要应用 std::move ,正如其他人已经指出的那样,它对性能优化。
例如
SomeClass fn()
{
...
}
// Move not needed, detrimental, as the function result is already an rvalue
SomeClass x = std::move(fn());
像往常一样在 c++11 之前这样做:
SomeClass x = fn();
一般来说当x有名字的时候使用std::move(x)。即它是一个标识符,除了特殊情况(例如,你想防止复制省略)外,它对临时对象没有用
旁注:也不要尝试使用 std::move 来强制 "strong exception gurantee"(例如,当你想要一个完全复制或单独保留的结构时 [即一致]潜在的异常。)因为 std::move 可以抛出取决于移动的类型,并且这种类型的异常策略可以根据你改变。而是使用带有静态断言的包装函数与 noexcept 相结合,以在编译时强制执行此操作。参见
下面的std::move
是多余的吗?
std::string member;
obj(std::initializer_list<std::string> p_list)
: member {std::move(join(p_list))}
{}
这是连接函数:
std::string join(string_initializer_list p_list) {
size_t size {};
for (auto const & s : p_list) {
size += s.size();
}
std::string output;
output.reserve(size);
for (auto const & s : p_list) {
output.append(s);
}
return output;
}
鉴于您的 join
功能正常,并且 return 和 std::string
,是的,std::move
是多余的; join
中的 return 已经是一个 r 值。
除此之外,w/o std::move
,复制省略意味着它可以在没有 std::move
的情况下就地构造结果,而使用 std::move
可以强制它生成一个临时的string
,调用移动构造函数初始化member
,然后销毁临时;工作量不大(主要是几个指针副本),但比您需要做的要多。
不,你不需要 std::move
。 std::move
的作用是将任意值转换为右值。您的函数已经 return 是一个右值,因此就将结果绑定到引用而言,强制转换没有任何效果(这就是您要从右值初始化 member
所追求的) .
实际上,主动使用std::move
抑制了拷贝省略,所以是严格的悲观化:
std::string s = join({}); // construct from prvalue, elidable,
// elision mandatory in C++17
std::string s = std::move(join({})); // temporary object must be constructed,
// s is initialized by moving from the
// temporary
在第一种形式中,std::string s = join({});
,复制省略意味着 join
的 returned 对象直接在 s
的位置构造(没有临时对象是构造并复制或移动),而且,函数体中的output
变量也被省略并直接在return值中构造,即在s
中。对于 std::move
,第一个省略步骤不可用。
为清楚起见:任何按值 returns 并因此 returns 右值的函数都不需要应用 std::move ,正如其他人已经指出的那样,它对性能优化。
例如
SomeClass fn()
{
...
}
// Move not needed, detrimental, as the function result is already an rvalue
SomeClass x = std::move(fn());
像往常一样在 c++11 之前这样做:
SomeClass x = fn();
一般来说当x有名字的时候使用std::move(x)。即它是一个标识符,除了特殊情况(例如,你想防止复制省略)外,它对临时对象没有用
旁注:也不要尝试使用 std::move 来强制 "strong exception gurantee"(例如,当你想要一个完全复制或单独保留的结构时 [即一致]潜在的异常。)因为 std::move 可以抛出取决于移动的类型,并且这种类型的异常策略可以根据你改变。而是使用带有静态断言的包装函数与 noexcept 相结合,以在编译时强制执行此操作。参见