1

Let's start with this small example:

#include <vector>
#include <iostream>

using namespace std;

class A {
private:
    A& operator =(const A&);
};

int main(void) {
    vector<A> v;
    v = { A() };
    return 0;
}

Compilation of this code fails with the error message error: ‘A& A::operator=(const A&)’ is private. I have no idea why it needs the assignment operator so I tried to find out and changed the code to this:

#include <vector>
#include <iostream>

using namespace std;

class A {
public:
    A& operator =(const A& a) { cout << "operator=" << endl; return *this; }
};

int main(void) {
    vector<A> v;
    v = { A() };
    return 0;
}

Now the code compiles but when I execute it it does not output the debug message in the assignment operator implementation.

So the compiler wants the assignment operator but doesn't use it? I guess the compiler optimizes the assignment away somehow. Just like it optimizes the usage of move constructors (Which can be prevented with the option -no-elide-constructors). Is there a compiler option which can prevent assignment optimimzations? Or is there a different explanation why the compiler wants to have an accessible assignment operator but doesn't use it during runtime?

kayahr
  • 20,913
  • 29
  • 99
  • 147
  • 2
    `std::vector` needs all of default constructor, copy constructor and assignment operator for contained types. You can't _optimize_ this contract away. – πάντα ῥεῖ Jul 15 '14 at 07:27
  • Also, move semantics and the mechanism affected by `-fno-elide-constructors` are two separate things. The latter omits constructor calls (possibly even move constructions) at the discretion of the compiler, while moves (all of them, not just those for constructors) happen at precisely defined places with precisely defined semantics. –  Jul 15 '14 at 07:33
  • @delnan But the move operator is also not called and the compiler doesn't complain when I define a private move operator so it looks like move semantics are not used in this code. I just mentioned the move constructor because I had a pretty similar problem yesterday where the compiler insisted on a public move constructor but didn't use it during runtime. That was because of this elide constructor optimization. I just thought that my current problem is also a compiler optimization which can be disabled. – kayahr Jul 15 '14 at 07:37
  • @Mark Garcia: I fixed the missing return statement. – kayahr Jul 15 '14 at 07:39
  • @delnan - Well, how would you enforce that copying is disabled in your `A` class? The classic and proven way is to define a private assignment operator (and copy constructor). Why would this method of turning off copying be taken away from the programmer? I would expect that the compiler give an error, else the compiler is broken. – PaulMcKenzie Jul 15 '14 at 07:47
  • @PaulMcKenzie: in C++11, we may use `= delete`. – Jarod42 Jul 15 '14 at 07:51
  • @Jarod42 - Yes, in C++ 11. But that doesn't mean that the "old-fashioned" way of turning off copying is deprecated or no longer present in C++ 11. – PaulMcKenzie Jul 15 '14 at 08:41
  • @πάντα ῥεῖ default constructor and copy constructor are not required in general. Only for certain vector operations. For objects that only have non-default and move constructor, you can put them in the vector using `emplace_back` . And the vector can be moved but not copied. – M.M Jul 15 '14 at 08:57
  • @PaulMcKenzie What does that have to do with *anything* I said? Anyway, it's a fine way to disable assignment constructors. Just don't be surprised when code that require assign-ability don't work with that type. –  Jul 15 '14 at 16:11
  • @delnan - My comments were meant for the OP. Sorry about that. Basically, the OP needs to answer the question of "how do you turn copying off if you wanted to?". – PaulMcKenzie Jul 15 '14 at 17:44

2 Answers2

1

In C++03, types being stored in a container need to be CopyConstructible and Assignable. In C++11, requirements are relaxed and applied to the operations performed on the container.

class A needs to be CopyConstructible and Assignable because being stored in vector That's why you need public operator=

int main(void) {
    vector<A> v;
    v = { A() }; // Copy Constructor

    A x;
    x = v[0]; // operator=
    return 0;
}
Alper
  • 12,860
  • 2
  • 31
  • 41
0

I little bit late but I still want to answer your question.

Your example shows a standard copy elision of C++. This is also discussed in another question.

That is, the compiler checks the correctness of your operation. You have to call a copy constructor right after default constructor to use vector and put your class inside, but call the default constructor only in order to improve performance.

C++ 11 solves the issue with the move constructor.