Non-Static Const Member, Can't Use Default Assignment Operator

Non-static const member, can't use default assignment operator

You have a case like this:

struct sample {
int const a; // const!

sample(int a):a(a) { }
};

Now, you use that in some context that requires sample to be assignable - possible in a container (like a map, vector or something else). This will fail, because the implicitly defined copy assignment operator does something along this line:

// pseudo code, for illustration
a = other.a;

But a is const!. You have to make it non-const. It doesn't hurt because as long as you don't change it, it's still logically const :) You could fix the problem by introducing a suitable operator= too, making the compiler not define one implicitly. But that's bad because you will not be able to change your const member. Thus, having an operator=, but still not assignable! (because the copy and the assigned value are not identical!):

struct sample {
int const a; // const!

sample(int a):a(a) { }

// bad!
sample & operator=(sample const&) { }
};

However in your case, the apparent problem apparently lies within std::pair<A, B>. Remember that a std::map is sorted on the keys it contains. Because of that, you cannot change its keys, because that could easily render the state of a map invalid. Because of that, the following holds:

typedef std::map<A, B> map;
map::value_type <=> std::pair<A const, B>

That is, it forbids changing its keys that it contains! So if you do

*mymap.begin() = make_pair(anotherKey, anotherValue);

The map throws an error at you, because in the pair of some value stored in the map, the ::first member has a const qualified type!

Type non-static const member can't use default assignment operator' - what does this mean?

Presumably, somewhere in your code you are assigning to a Member, something like this:

Member m1, m2;
m1 = m2;

Or you are using it in a context that requires the type to be assignable.

Since you don't provide your own assignment operator overload for Member, the implicitly defined defaulted assignment operator would usually kick in. However, since you have a const data member, the compiler won't implicitly define one for you. You need to provide it yourself.

This makes sense because, imagine in the code sample I just gave, what should the compiler do to m1's membershipNo member? Should it assign m2's membershipNo to it? How can it do that if membershipNo is const? In this case, the compiler just says "Nope, I can't do it."

A defaulted copy/move assignment operator for class X is defined as deleted if X has:

  • a non-static data member of const non-class type (or array thereof)
  • [...]

As I said, to be able to do assignment with a Member, you need to provide an assignment operator of your own:

Member& Member::operator=(const Member& other)
{
// Copy things from other to this
}

However, herein lies the problem with having const members. If you supply your own copy assignment operator as described, and you don't copy over membershipNo, then have you really copied the other object? Logically, it makes sense that an object that has any const state shouldn't be able to be assigned to.

It is perfectly fine to use a copy constructor however - you only have to make sure you initialise membershipNo in the member initialization list:

Member::Member(const Member& other)
: membershipNo(other.membershipNo)
{
// ...
}

And then you can do:

Member m1;
Member m2 = m1;

In C++, can a class with a const data member not have a copy assignment operator?

In C++, a class with a const data member may have a copy constructor.

#include <iostream>

class A
{
private:
const int k_;
public:
A(int k) : k_(k) {}
A() = delete;
A(const A& other) : k_(other.k_) {}

int get_k() const { return k_; }
};

int main(int argc, char** argv)
{
A a1(5);
A a2(a1);

std::cout << "a1.k_ = " << a1.get_k() << "\n";
std::cout << "a2.k_ = " << a2.get_k() << "\n";
}

Output:

a1.k_ = 5
a2.k_ = 5

In C++, a class with a const data member may not use the default assignment operator.

class A
{
private:
const int k_;
public:
A(int k) : k_(k) {}
A() = delete;
A(const A& other) : k_(other.k_) {}

int get_k() const { return k_; }
};

int main(int argc, char** argv)
{
A a1(5);
A a2(0);

a2 = a1;
}

Yields a compile time error:

const_copy_constructor.cpp: In function ‘int main(int, char**)’:
const_copy_constructor.cpp:18:10: error: use of deleted function ‘A& A::operator=(const A&)’
18 | a2 = a1;
| ^~
const_copy_constructor.cpp:1:7: note: ‘A& A::operator=(const A&)’ is implicitly deleted because the default definition would be ill-formed:
1 | class A
| ^
const_copy_constructor.cpp:1:7: error: non-static const member ‘const int A::k_’, can’t use default assignment operator

In C++, a class with a const data member may use a non-default assignment operator as long as you don't attempt to change the const data member, but you better think long and hard about what it means to use this assignment operator if one of the underlying members cannot be modified.

class A
{
private:
const int k_;
public:
A(int k) : k_(k) {}
A() = delete;
A(const A& other) : k_(other.k_) {}

A& operator=(A const& other)
{
// do nothing
return *this;
}

int get_k() const { return k_; }
};

int main(int argc, char** argv)
{
A a1(5);
A a2(0);

a2 = a1;
}

Yields no compile time errors.

non-static reference member, can't use default assignment operator'

No default assignment operator was created for your class Timestep because it contains a reference (which cannot be set later. it basically is a constant pointer to non-const data).
solution.push_back(myFirstTimestep) requires the asignment though (or move with c++11) so you will have to define your own assignment (or move) operator (which of course you will not be able to do unless you change Timestep& previousTimestep to Timestep *previousTimestep in which case the default assignment will work as well).

error: non-static reference member - can't use default assignment operator

What does this error mean?

It means what it says literally. The assignment operator of A and consequently Handler cannot be used.

Important points:

  • References are not assignable.
  • The class contains a reference member, and no user defined assignment operator. Therefore the class is not assignable.
  • You attempt to assign objects of that class.

What is the problem?

You attempt to assign a non-assignable class.

Your options are:

  • Don't use a reference member. Use a pointer or a reference wrapper (standard library has one since C++11) both of which are assignable.
  • Don't assign the object.

Error: non-static reference member, can't use default assignment operator

The WiFly member of your WiFlyClient is making the class not assignable. The reason for that is that assignment cannot be used to change which object the reference is referring to. For example:

int a = 1;
int b = 2;
int &ar = a;
int &br = b;
ar = br; // changes a's value to 2, does not change ar to reference b

Since all your WiFlyClients are referencing the same WiFlyDevice instance, you can change WiFlyClient as the compiler suggests to use a static member:

// WiFlyClient.h
class WiFlyClient : public Client {
public:
WiFlyClient();
...
private:
static WiFlyDevice& _WiFly;
...
};

Then, you do not initialize it in the constructor, but in a source file where you define it.

WiFlyDevice & WiFlyClient::_WiFly = WiFly;

non-static reference member ‘int& Property int ::value’, can’t use default assignment operator

As the compiler said, non-static reference cannot be assigned using the compiler-generated assignment operator. A user-defined copy assignment operator is required.

Your user-defined assignment operator performs assignment to the object referenced by value, by calling first your operator T() to obtain a temporary value of type T and then your Property<T>& operator=(T a) to execute value = a, which replaces the referenced-to object with the value obtained from the conversion operator.

If nothing is derived from your class, your operator= is equivalent to

// uncommenting this eliminates the error
Property<T>& operator=(const Property<T>& newval)
{
// return (*this = T(newval)); // convert to T, then assign
value = newval.value; // assign the referenced object
return *this;
}

What is the point of non-static const data member?

A const data member does not have to be the same for all instances. You can initialize it in the constructor.

class A
{
public:
A(int n) :cx(n) {}

const int cx;
};

int main()
{
A a1(10);
A a2(100);
}

Deleted assignment operator error which isn't used

Consider how the erase function might be implemented. It might look something like this:

constexpr iterator erase(iterator pos) {
for (auto i = pos; i + 1 != end(); i++) {
*i = move(*(i + 1));
}
pop_back();
return pos;
}

(The real parameter type is const_iterator, but let's ignore this detail.)

If you choose to erase the last element of the vector, then the loop will run zero times and the assignment operator will be invoked zero times. However, you still force the compiler to instantiate the entire body of the erase function. When this happens, the compiler must perform overload resolution and check whether there is a usable assignment operator. If there is not, a compilation error occurs.

Expecting your program to compile is like expecting this program to compile:

void foo() = delete;
void bar(int num_times) {
while (num_times--) foo();
}
int main() {
bar(0);
}

Would this program compile? Of course not. Even though foo will never be invoked, the mere fact that you attempt to compile some code that contains a call to foo will make the program ill-formed.

With a std::vector, it is possible for you to say "I promise that I will only be removing the last element, so please do not attempt to compile any code that requires the assignment operator". To do this, use the pop_back function instead of erase.



Related Topics



Leave a reply



Submit