-1

This is not a duplicate of the question attached to the close request.

I have a class with a const-qualified field. I'm trying to assign it to a data structure that already has a value in the place I'm assigning. But because my class has a const-qualified field, my compiler is not allowing me to do so.


Errors:

Depending on the compiler, I either get:

note: copy assignment operator of 'A' is implicitly deleted because field 'size' is of const-qualified type 'const unsigned short'

or:

error: non-static const member ‘const short unsigned int A::size’, cannot use default assignment operator

Which are basically the same thing.


Minimal Viable Example:

#include <iostream>
#include <vector>
class A {
    private:
        const unsigned short size;
     
    public:
    
        A(unsigned short sz) : size(sz) {}
        
        void print_sz() {
            std::cout<<this->size<<"\n";
        }
};

int main()
{
    A a = A(2);
    
    std::vector<A> a_vec = std::vector<A>();
    a_vec.push_back(A(0));
    a_vec[0] = A(1); // This line breaks my code
    
    a_vec[0].print_sz(); // I want to see 1 printed here
    return 0;
}

This doesn't make sense to me... why can't it implicitly copy over a const value?

Who cares if it's const, just make a copy of the damn short!

I've tried std::move and so many other things

I don't want to implement a copy assignment operator and copy each member by hand because there's so many fields and a dynamic array in my actual code that I know I'll mess something up if I try to implement it.

I just want to put an A into my vector...


Questions:

1 - How do I fix this? I want any quick hack that works and doesn't require implementing a copy assignment operator. I don't care about the element already in the container. I just want to move the data from the temporary stack into the vector (to use elsewhere)

2 - If you have the time, I'd love an explanation as to why this makes sense...


Thanks.

JoeVictor
  • 1,806
  • 1
  • 17
  • 38
  • 1
    The assignment `operator=` is deleted because by default it just copies/assigns to each data member of the class. But since a `const` variable cannot be assigned to, there is no poing in having an assignment operator(which was supposed to do the assignment). – Jason Mar 25 '23 at 07:10
  • 1
    *I don't care about the element already in the container* -- Forgetting about the `const` aspect of this, the purpose of an assignment operator is one thing and one thing only -- to assign one object to another., nothing more, nothing less. As soon as you put "business logic" into the assignment operator, you risk creating copies that are not actually copies. This opens you up for one of the hardest bugs to track down, and that is one where objects that are supposed to be equal are not equal. Remember that the compiler will also call the assignment operator, not just by you explicitly. – PaulMcKenzie Mar 25 '23 at 07:17
  • 2
    As a rule of thumb - avoid `const` modifier for member variables, as their constness should be managed by the constness of the owning object – The Dreams Wind Mar 25 '23 at 07:18
  • If you need to do a "weird" assignment, write an `assign()` function. Do not use `operator=` for this. Again, using the fundamental assignment operator to have logic that will produce side-effects will end in tears. Also, your example doesn't even need vector: `A a(2); A anotherA(3); a = anotherA;` – PaulMcKenzie Mar 25 '23 at 07:18

2 Answers2

2

You can't do assignment here. You've specified that size is const, which means you can initialize it, but you can't assign to it.

One (kind of ugly) workaround would be to destroy the existing object, then use placement new to create a new object with the new value in the same location.

#include <iostream>
#include <vector>
class A {
    private:
        const unsigned short size;
     
    public:
    
        A(unsigned short sz) : size(sz) {}
        
        void print_sz() {
            std::cout<<this->size<<"\n";
        }
};

int main()
{
    A a = A(2);
    
    std::vector<A> a_vec = std::vector<A>();
    a_vec.push_back(A(0));
    a_vec[0].~A();        // out with the old
    new (&a_vec[0]) A(1); // and in with the new
    
    a_vec[0].print_sz(); // Prints `1`
    return 0;
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I like this answer. But one question: your `new` call here introduces a memory leak right? or does the vector container manage the newly allocated memory in its destructor? – JoeVictor Mar 25 '23 at 07:24
  • 1
    @JoeVictor: No, a placement `new` doesn't allocating any memory--it just constructs an object in the specified location. – Jerry Coffin Mar 25 '23 at 07:25
  • Amazing. I learned something new *and* I solved my problem. Thanks! – JoeVictor Mar 25 '23 at 07:27
  • 2
    In C++20 we have [std::construct_at](https://en.cppreference.com/w/cpp/memory/construct_at) which is a slightly more obvious way of doing the same thing. There is also `std::destroy_at` from C++17. – john Mar 25 '23 at 07:39
  • awesome. thanks, will need to look into upgrading from C++14 then! – JoeVictor Mar 25 '23 at 09:45
1

The assignment operator= is deleted because by default it just assigns to each member of the class. But since a const variable cannot be assigned to, there is no point in having the implicit assignment operator. This goes for both copy assignment as well as move assignment.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • How do I tell the compiler to: "move all the contents of the temp variable to `a_vec[0]`"? – JoeVictor Mar 25 '23 at 07:12
  • 1
    @JoeVictor You can't! As the move assignment operator can also not be used here. Trying to change a const variable is **undefined behavior**. Just don't try to change a const variable. – Jason Mar 25 '23 at 07:14