Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

explicit copy constructor or implicit parameter by value

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?

like image 461
R Samuel Klatchko Avatar asked Jan 09 '10 19:01

R Samuel Klatchko


People also ask

Can copy constructor be pass by value?

In C++, a Copy Constructor may be called in the following cases: When an object of the class is returned by value. When an object of the class is passed (to a function) by value as an argument. When an object is constructed based on another object of the same class.

What do you mean by explicit constructor?

The explicit function specifier controls unwanted implicit type conversions. It can only be used in declarations of constructors within a class declaration. For example, except for the default constructor, the constructors in the following class are conversion constructors.

What is implicit copy constructor?

An implicitly defined copy constructor will copy the bases and members of an object in the same order that a constructor would initialize the bases and members of the object.

What is explicit and implicit call in C++?

In programming, implicit is often used to refer to something that's done for you by other code behind the scenes. Explicit is the manual approach to accomplishing the change you wish to have by writing out the instructions to be done explicitly.


2 Answers

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.

like image 193
jamesdlin Avatar answered Sep 22 '22 08:09

jamesdlin


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");
}
like image 34
CB Bailey Avatar answered Sep 22 '22 08:09

CB Bailey