简体   繁体   English

为什么复制elision不能使用std :: move?

[英]Why copy elision not working with std::move?

I use the code below to test copy elision: 我使用下面的代码来测试copy elision:

class foo
{
public:
    foo() {cout<<"ctor"<<endl;};
    foo(const foo &rhs) {cout<<"copy ctor"<<endl;}
};

int g(foo a)
{
    return 0;
}

int main()
{
    foo a;
    g(std::move(a));
    return 0;
}

I expected only the default constructor would be called because the argument of g() is an rvalue and copy will be elided. 我预计只会调用默认构造函数,因为g()的参数是一个rvalue,并且将省略复制。 But the result shows that both the default constructor and the copy constructor are called. 但结果显示默认构造函数和复制构造函数都被调用。 Why? 为什么?

And if I change the function call to g(foo()) , the copy will be elided. 如果我将函数调用更改为g(foo()) ,则复制将被删除。 What's the difference between the return types of foo() and std::move(a) ? foo()std::move(a)的返回类型之间有什么区别? How can I make the compiler elide copy on an lvalue? 如何在左值上编译编译器?

Copy elision for can only occur in a few specific situations, the most common of which is the copying of a temporary (the others are returning locals, and throwing/catching exceptions). 复制省略只能在少数特定情况下发生,其中最常见的是复制临时(其他人正在返回本地,以及抛出/捕获异常)。 There is no temporary being produced by your code, so no copy is elided. 您的代码没有临时生成,因此不会删除任何副本。

The copy constructor is being called because foo does not have a move constructor (move constructors are not implicitly generated for classes with explicit copy constructors), and so std::move(a) matches the foo(const foo &rhs) constructor (which is used to construct the function argument). 正在调用复制构造函数,因为foo没有移动构造函数(移动构造函数不是为具有显式复制构造函数的类隐式生成的),因此std::move(a)匹配foo(const foo &rhs)构造函数(这是用于构造函数参数)。

A copy of an lvalue can be elided in the following situations (although there is no way to force a compiler to perform the elision): 在以下情况下可以省略左值的副本(尽管无法强制编译器执行省略):

foo fn() {
    foo localAutomaticVariable;
    return localAutomaticVariable; //Copy to construct return value may be elided
}

int main() {
    try {
        foo localVariable;
        throw localVariable; //The copy to construct the exception may be elided
    }
    catch(...) {}
}

If you want to avoid copies when passing function arguments, you can use a move constructor which pilfers the resources of the objects given to it: 如果要在传递函数参数时避免复制,可以使用移动构造函数来传递给定对象的资源:

class bar {
public:
    bar() {cout<<"ctor"<<endl;};
    bar(const bar &rhs) {cout<<"copy ctor"<<endl;}
    bar(bar &&rhs) {cout<<"move ctor"<<endl;}
};

void fn(bar a)
{
}
//Prints:
//"ctor"
//"move ctor"
int main()
{
    bar b;
    f(std::move(b));
}

Also, whenever copy elision is allowed but does not occur, the move constructor will be used if it is available. 此外,每当允许复制省略但未发生复制省略时,将使用移动构造函数(如果可用)。

You need to declare g as: 您需要将g声明为:

int g(foo && a) //accept argument as rvalue reference
{
    return 0;
}

Now it can accept argument by rvalue-reference. 现在它可以通过rvalue-reference接受参数。

In your case, even though the expression std::move(a) produces rvalue, it doesn't bind to a parameter which accepts argument by value . 在您的情况下,即使表达式std::move(a)产生rvalue,它也不会绑定到按值接受参数的参数 The receiving end must be rvalue-reference as well. 接收端也必须是rvalue-reference

In case of g(foo()) , the copy-elision is performed by the compiler, which is an optimization. g(foo())情况下,复制省略由编译器执行,这是一种优化。 It is NOT a requirement by the language [until C++17] . 它不是语言的要求 [直到C ++ 17] You can disable this optimization if you want to : then g(foo()) and g(std::move(a)) will behave exactly same, as expected. 如果您愿意,可以禁用此优化:然后g(foo())g(std::move(a))将按预期完全相同。

But if you change g as I suggested above, the call g(foo()) will not make a copy because it is a requirement by the language to not make copy with && . 但是如果按照我上面的建议更改g ,则调用g(foo())将不会复制,因为语言要求不使用&&进行复制 It is not a compiler-optimization anymore. 它不再是编译器优化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM