Auto Gcroot Assignment Operator Could Not Be Generated

The latest version of this topic can be found at Compiler Warning (level 4) C4512.

class' : assignment operator could not be generated

The compiler cannot generate an assignment operator for the given class. No assignment operator was created.

An assignment operator for the base class that is not accessible by the derived class can cause this warning.

To avoid this warning, specify a user-defined assignment operator for the class.

The compiler will also generate an assignment operator function for a class that does not define one. This assignment operator is a memberwise copy of the data members of an object. Because data items cannot be modified after initialization, if the class contains a item, the default assignment operator would not work. Another cause of the C4512 warning is a declaration of a nonstatic data member of reference type. If the intent is to create a non-copyable type, you must also prevent the creation of a default copy constructor.

You can resolve the C4512 warning for your code in one of three ways:

  • Explicitly define an assignment operator for the class.

  • Remove const or the reference operator from the data item in the class.

  • Use the #pragma warning statement to suppress the warning.

The following sample generates C4512.

// C4512.cpp // compile with: /EHsc /W4 // processor: x86 class Base { private: const int a; public: Base(int val = 0) : a(val) {} int get_a() { return a; } }; // C4512 warning class Base2 { private: const int a; public: Base2(int val = 0) : a(val) {} Base2 & operator=( const Base2 & ) { return *this; } int get_a() { return a; } }; // Type designer intends this to be non-copyable because it has a // reference member class Base3 { private: char& cr; public: Base3(char& r) : cr(r) {} // Uncomment the following line to explicitly disable copying: // Base3(const Base3&) = delete; }; // C4512 warning int main() { Base first; Base second; // OK Base2 first2; Base2 second2; char c = 'x'; Base3 first3(c); Base3 second3 = first3; // C2280 if no default copy ctor }

What does this warning mean, and how do you fix it?

warning C4512: '<some type>' : assignment operator could not be generated

The compiler will auto-generate some class members for you

  • default constructor (if no other constructor is explicitly declared)
  • destructor
  • copy constructor (if no move constructor or move assignment operator is explicitly declared)
  • copy assignment operator (if no move constructor or move assignment operator is explicitly declared)

C++ 11 added two new auto-generated class members (and it added “if destructor then copy constructor and copy assignment operator generation is deprecated”):

  • move constructor (if no copy constructor, move assignment operator or destructor is explicitly declared)
  • move assignment operator (if no copy constructor, copy assignment operator or destructor is explicitly declared)

Compiler-generated functions are public and non-virtual. As a reminder, here are the signatures of all of these functions:

class Object { Object(); // default constructor Object(const Object& other); // copy constructor Object(Object&& other); // move constructor Object& operator=(const Object& other); // copy assignment operator Object& operator=(Object&& other); // move assignment operator ~Object(); // destructor };

So, what if you can’t actually create a meaningful copy assignment operator? For example, if you have const data, you can’t assign to it. Remember that the auto-generated copy assignment operator just generates assignment operator code for each member of the class, recursively, and you can’t assign to const int, you can only construct it.

struct ConstantOne { ConstantOne() : value(1) {} const int value; }; int main(int /*argc*/, char ** /*argv*/) { ConstantOne b; return 0; }

This will give you a warning when you compile, because the auto-generated assignment operator is technically illegal, and so the compiler won’t generate it. It’s a warning, because your code probably doesn’t need an assignment operator. For Visual C++, you’ll see something like this:

warning C4512: 'ConstantOne' : assignment operator could not be generated

You have several options. The easiest is just to declare an assignment operator without a body. As long as you never actually try to use the assignment operator, you’ll be fine. And, the contract for this object says that assignment would be illegal anyway, so you’ll get a valid-to-you compile error if you accidentally try to use it.

struct ConstantOne { ConstantOne() : value(1) {} const int value; private: ConstantOne& operator=(const ConstantOne&); }; int main(int /*argc*/, char ** /*argv*/) { ConstantOne b; ConstantOne c; c = b; return 0; }

The standard is to make these private, to reinforce that they are not meant to be used. If you compile code with an assignment operator, you’ll get a compile-time error.

error C2248: 'ConstantOne::operator =' : cannot access private member declared in class 'ConstantOne'

And in C++11, there’s even a keyword to add here to declare that it indeed should not be allowed:

struct ConstantOne { ConstantOne() : value(1) {} const int value; ConstantOne& operator=(const ConstantOne&) = delete; };

Note that you don’t need the trickery of making it private, and you get a nicer compile-time error if you try to use the assignment operator.

This happens in big real-world projects quite often. In fact, it happens enough that the delete keyword was added in C++11. Visual Studio 2013 and up, GCC 4.7 and up, and Clang 2.9 and up support the delete and default keywords.

Now, there is another approach to the assignment operator when you have const data – generate an assignment operator that can write to const data with const_cast. You’re almost always doing the wrong thing if you do this, but sometimes it has to be done anyway. It looks horrible, and it is horrible. But maybe it’s necessary.

struct ConstantOne { ConstantOne() : value(1) {} const int value; ConstantOne& operator=(const ConstantOne& that) { if (this != &that) { *const_cast(&this->value) = that.value; } return *this; } }; int main(int /*argc*/, char ** /*argv*/) { ConstantOne b; ConstantOne c; c = b; return 0; }

The reason this is horrible is that you are violating the contract – you’re altering a constant value in the LHS of the equation. Depending on your circumstance, that can still be a correct thing to do.

Categories: 1

0 Replies to “Auto Gcroot Assignment Operator Could Not Be Generated”

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *