mar*_*ark 18 c++ move-semantics c++11
继我对此做出的评论之后:
将std :: vector传递给构造函数并移动语义
是否std::move
在下面的代码中是必要的,以确保返回的值是xvalue?
std::vector<string> buildVector()
{
std::vector<string> local;
// .... build a vector
return std::move(local);
}
Run Code Online (Sandbox Code Playgroud)
我的理解是这是必需的.我经常看到std::unique_ptr
从函数返回时使用的这个,但是GManNickG发表了以下评论:
我的理解是,在一个return语句中,所有局部变量都是自动xvalues(到期值)并将被移动,但我不确定它是否仅适用于返回的对象本身.所以OP应该继续把它放在那里,直到我更加自信它不应该是.:)
任何人都可以澄清是否std::move
有必要吗?
行为编译器是否依赖?
Ker*_* SB 15
您可以保证local
在这种情况下将作为右值返回.通常编译器会执行返回值优化,但在此之前甚至会成为问题,并且您可能根本看不到任何实际移动,因为local
对象将直接在调用站点构建.
6.6.3中的相关说明 ["退货声明"](2):
在选择构造函数(12.8)时,为了重载解析的目的,可以省略或将与返回语句相关联的复制或移动操作视为右值.
为了澄清,这就是说返回的对象可以从本地对象移动构造(即使实际上RVO将完全跳过此步骤).标准的规范部分是12.8 ["复制和移动类对象"](31,32),复制省略和rvalues(感谢@Mankarse!).
这是一个愚蠢的例子:
#include <utility>
struct Foo
{
Foo() = default;
Foo(Foo const &) = delete;
Foo(Foo &&) = default;
};
Foo f(Foo & x)
{
Foo y;
// return x; // error: use of deleted function ‘Foo::Foo(const Foo&)’
return std::move(x); // OK
return std::move(y); // OK
return y; // OK (!!)
}
Run Code Online (Sandbox Code Playgroud)
将此与返回实际右值参考进行对比:
Foo && g()
{
Foo y;
// return y; // error: cannot bind ‘Foo’ lvalue to ‘Foo&&’
return std::move(y); // OK type-wise (but undefined behaviour, thanks @GMNG)
}
Run Code Online (Sandbox Code Playgroud)
尽管两者都是,return std::move(local)
并且return local
在他们编译的意义上工作,但他们的行为是不同的.并且可能只有后一个是有意的.
如果你编写一个返回a的函数std::vector<string>
,你必须返回一个 std::vector<string>
完全正确的函数.std::move(local)
具有类型std::vector<string>&&
是不是一个std::vector<string>
所以它必须使用移动构造被转换到它.
标准在6.6.3.2中说:
表达式的值隐式转换为它出现的函数的返回类型.
这意味着,return std::move(local)
是平等的
std::vector<std::string> converted(std::move(local); // move constructor
return converted; // not yet a copy constructor call (which will be elided anyway)
Run Code Online (Sandbox Code Playgroud)
而return local
只是
return local; // not yet a copy constructor call (which will be elided anyway)
Run Code Online (Sandbox Code Playgroud)
这使您免于一次操作.
给你一个简短的例子:
struct test {
test() { std::cout << " construct\n"; }
test(const test&) { std::cout << " copy\n"; }
test(test&&) { std::cout << " move\n"; }
};
test f1() { test t; return t; }
test f2() { test t; return std::move(t); }
int main()
{
std::cout << "f1():\n"; test t1 = f1();
std::cout << "f2():\n"; test t2 = f2();
}
Run Code Online (Sandbox Code Playgroud)
这将输出
f1():
construct
f2():
construct
move
Run Code Online (Sandbox Code Playgroud)
我认为答案是否定的。虽然官方只是一个注释,§5/6 总结了哪些表达式是/不是 xvalues:
表达式是 xvalue,如果它是:
- 调用函数的结果,无论是隐式还是显式,其返回类型是对对象类型的右值引用,
- 转换为对对象类型的右值引用,
- 指定非引用类型的非静态数据成员的类成员访问表达式,其中对象表达式是 xvalue,或
- .* 成员指针表达式,其中第一个操作数是 xvalue,第二个操作数是指向数据成员的指针。
一般来说,这条规则的作用是,命名的右值引用被视为左值,而对对象的未命名右值引用被视为xvalues;无论是否命名,对函数的右值引用都被视为左值。
第一个要点似乎适用于此。由于所讨论的函数返回的是值而不是右值引用,因此结果不会是 xvalue。