explicit copy constructor or implicit parameter by value

孤人 提交于 2019-12-30 00:07:10

问题


I recently read (and unfortunately forgot where), that the best way to write operator= is like this:

foo &operator=(foo other)
{
    swap(*this, other);
    return *this;
}

instead of this:

foo &operator=(const foo &other)
{
    foo copy(other);
    swap(*this, copy);
    return *this;
}

The idea is that if operator= is called with an rvalue, the first version can optimize away construction of a copy. So when called with a rvalue, the first version is faster and when called with an lvalue the two are equivalent.

I'm curious as to what other people think about this? Would people avoid the first version because of lack of explicitness? Am I correct that the first version can be better and can never be worse?


回答1:


You probably read it from: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

I don't have much to say since I think the link explains the rationale pretty well. Anecdotally I can confirm that the first form results in fewer copies in my builds with MSVC, which makes sense since compilers might not be able to do copy-elision on the second form. I agree that the first form is a strict improvement and is never worse than the second.

Edit: The first form might be a bit less idiomatic, but I don't think it's much less clear. (IMO, it's not any more surprising than seeing the copy-and-swap implementation of the assignment operator for the first time.)

Edit #2: Oops, I meant to write copy-elision, not RVO.




回答2:


I generally prefer the second one from readability and 'least surprise' point of view, however I do acknowledge that the first one can be more efficient when the parameter is a temporary.

The first one really can lead to no copies, not just the single copy and it's conceivable that this may be a genuine concern in extreme situations.

E.g. Take this test program. gcc -O3 -S (gcc version 4.4.2 20091222 (Red Hat 4.4.2-20) (GCC)) generates one call to B's copy constructor but no calls to A's copy constructor for the function f (the assignment operator is inlined for both A and B). A and B can both be taken to be very basic string classes. Allocation and copying for data would occur in the constructors and deallocation in the destructor.

#include <algorithm>

class A
{
public:
    explicit A(const char*);
    A& operator=(A val)      { swap(val); return *this; }
    void swap(A& other)      { std::swap(data, other.data); }
    A(const A&);
    ~A();

private:
    const char* data;
};

class B
{
public:
    explicit B(const char*);
    B& operator=(const B& val)  { B tmp(val); swap(tmp); return *this; }
    void swap(B& other)         { std::swap(data, other.data); }
    B(const B&);
    ~B();

private:
    const char* data;
};

void f(A& a, B& b)
{
    a = A("Hello");
    b = B("World");
}



回答3:


Given this

foo &foo::operator=(foo other) {/*...*/ return *this;}
foo f();

in code like this

foo bar;
bar = f();

it might be easier for a compiler to eliminate the call to the copy constructor. With RVO, it could use the address of the operator's other parameter as the place for f() to construct its return value at.

It seems that this optimization is also possible for the second case, although I believe it might be harder. (Especially when the operator isn't inlined.)




回答4:


These two are actually the same. The only difference is where you press "Step In" in a debugger. And you should know in advance where to do it.




回答5:


I think that you might be confusing the difference between:

foo &operator=(const foo &other); and
const foo &operator=(const foo &other);

The first form should be used to allow for: (a = b) = c;



来源:https://stackoverflow.com/questions/2034635/explicit-copy-constructor-or-implicit-parameter-by-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!