0

There's something I don't understand about how the assignment operator works, please have a look at this piece of code:

#include <iostream>
using namespace std;

class foo
{
    int val{};
public:
    foo()
    {
    }
    foo( int n )
    {
        val = n;
        cout<<"Constructor"<<endl;
    }
    foo( const foo& f )
    {
        cout<<"Copy constructor, val "<<endl;
        val = f.val;
    }

    foo( const foo&& f )
    {
        cout<<"Copy constructor -rvalue-"<<endl;
        val = f.val;
    }
    foo operator+( const foo& other ) const
    {
        cout<<"Sum "<<endl;
        foo foo2;
        foo2.val = val + other.val;
        return foo2;
    }
    foo& operator=( const foo& f )
    {
        cout<<"Assignment operator\n";
        val = f.val;
        return *this;
    }
    foo& operator=( const foo&& f)
    {
        cout<<"Assignment operator, r-value\n";
        val = f.val;
        return *this;
    }
    ~foo() {}
};

int main()
{
    foo a{1}, b{5}, c{4};
    foo d;
        d = a + b + c;
    foo d2 = a + b + c;
    return 0;
}

The output of this application is:

Constructor
Constructor
Constructor
Sum
Sum
Assignment operator, r-value
Sum
Sum

What is not clear to me is why the second assignment trigger no assignment operator. The first assignment is on an object which was constructed via the default construction and then a plain assignment operation is visible, in the second one a temporary object should be builded by the compiler and then assigned to d2, but no print are visible from any of the assignment operators provided. Why?

Thanks

Wolf
  • 9,679
  • 7
  • 62
  • 108
fjanisze
  • 1,234
  • 11
  • 21

4 Answers4

3

There's no second assignment, your code has only one assignment. The line

foo d2 = a + b + c;

is copy initialization.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
1

For efficiency's sake, this:

foo d2 = a + b + c;

(and any T n = expr where T is a type with a non-explicit copy constructor) is actually synonymous for

foo d2(foo(a + b + c)); // can be optimized to foo d2(a + b + c)

not

foo d2;
d2 = a + b + c;

Which is why you only see one assignment in your output.

You can think of it this way: when the variable already exists, it's assignment. When you're creating a new variable, it's a constructor call.

uk4321
  • 1,028
  • 8
  • 18
  • It has nothing to do with efficiency. It's just the way the language is specified. The `=` character can be an operator, or punctuation. In this case, it's punctuation, not the assignment operator. – James Kanze Nov 17 '13 at 20:01
  • @JamesKanze I meant that the language was specified that way for efficiency. It could just as well have been the other way, and it would even be more intuitive, but it would be inefficient to do make it a separate construction + assignment. Unless you know of some other reason. – uk4321 Nov 17 '13 at 20:03
  • I think is almost clear, the last think i dont understand now is why foo d2(foo(a + b +c)) does not invoke one of my pre-defined constructors, in this case like foo( const foo& f )? The only print visible are those about the sum operation. – fjanisze Nov 17 '13 at 20:18
  • @uk4321 copy- and direct-initialization are not synonyms. They have different semantics. – jrok Nov 17 '13 at 20:22
  • @jrok how can `foo d2(foo(x))` and `foo d2 = x` differ? – uk4321 Nov 17 '13 at 20:31
  • @user1882090 I can only think that the compiler is optimizing some calls away. – uk4321 Nov 17 '13 at 20:35
  • @uk4321 See [this example](http://coliru.stacked-crooked.com/a/4b43890bb3c3fb79) and more in depth [here](http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-direct-initializati). – jrok Nov 17 '13 at 20:38
  • @jrok I hadn't bothered about that for this example, I added a note about it. – uk4321 Nov 17 '13 at 20:39
  • @uk4321 The main reason, I think, is that the language does distinguish initialization from assignment, and that there is no requirement that an object have a default constructor in something like `MyType x = y;`. Requiring a default constructor in such cases would be very limiting. – James Kanze Nov 18 '13 at 11:52
  • @James: So what is happening exactly? Since no constructor is involved in MyType x = y how can i built this object is user-defined way? -no one of my operators/constructors are invoked now-, the values from ( a + b + c ) are memberwise copied to d2 by the compiler? Is not possible to user-define this operation? – fjanisze Nov 18 '13 at 13:43
  • @fjanisze What do you mean no constructor is involved? The whole point is that no assignment operator is involved, so that there is no need to default construct `x` before invoking the assignment operator. – James Kanze Nov 18 '13 at 13:55
  • @JamesKanze well, it wouldn't be very limiting because you could just do `MyType x(y)`. And then you'd have ways to do both (default construct, or copy construct) and the syntax wouldn't be ambiguous. – uk4321 Nov 26 '13 at 20:40
  • @uk4321 I forget the motivation for having two different types of construction, but they do have different semantics, and thus different syntaxes. And the `=` isn't really ambiguous here, any more than a lot of other constructs. The real ambiguity is things like `MyType x(Y());`, where `Y` is the name of a class. – James Kanze Nov 27 '13 at 10:49
  • @JamesKanze yes that is annoying, it makes one thankful for the new `MyType x(Y{})` syntax. Was there any other difference in semantics between the two than the requirement of a non-explicit copy ctor for the `=` version? It seems like that was just added because they knew it looked like `operator=` and didn't want to implicitly construct an object for you. So it seems like the reason comes back to efficiency. And I meant that the `=` is ambiguous because it looks like it should call `operator=` which it doesn't. You can tell by all the "Why doesn't this code call operator=?" here on SO. – uk4321 Nov 27 '13 at 18:43
  • @uk4321 I forget the exact reasons for wanting two different semantics, but I do remember asking, and receiving an answer which convinced me at the time. (That was over 20 years ago.) As for the "why doesn't this code call `operator=`" questions: one does wonder where some of the posters here are learning C++. (Given some of the "homework" questions, and what they reveal about the course, one can conclude that there are a lot of people teaching C++ who are completely incompetent.) – James Kanze Nov 28 '13 at 08:58
0

You're initializing d2, not assigning to it, so the assignment operator isn't called.

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
0

The first is assignment, the second is initialization. And in that case the compiler is allowed to elide the copy or move operations that can occur in such a case.

David G
  • 94,763
  • 41
  • 167
  • 253
  • Can you clarify what do you mean by elide? Is in a way that no pre-defined operator/constructor is used and the compiler just copy the values -memberwise- from the temporary object -a+b+c- to d2? – fjanisze Nov 18 '13 at 13:54
  • `a + b + c` returns an rvalue, `r`, so `foo d2 = r` should call the move constructor but because of [copy-elision](http://en.wikipedia.org/wiki/Copy_elision), the compiler is allowed to omitt the call. I don't know to much about this but I would encourage reasearching about it. – David G Nov 18 '13 at 14:21