2

I'm trying to replace a struct value that has a const value in a non const array but it fails to compile with:

Object of type 'Foo' cannot be assigned because its copy assignment operator is implicitly deleted

Here is an example:

struct Foo {
    const int id;
    int value;
    Foo(): id(-1), value(-1) {}
    Foo(int id): id(id), value(0) {}
};


int main(int argc, const char * argv[]) {
    Foo foos[10];
    foos[0] = Foo(1234);
    return 0;
}

I'm coming from a swift background where that is valid as it copies the struct value into the array.

I tried making what I thought was a copy assignment operator but with const members what I tried didn't work either.

This is what I was trying to do for the copy assignment operator:

    Foo& operator= (Foo newVal) {
        // what do I put here!?
        return *this;
    };

Doing a memcpy to the array works but seems like the wrong sledgehammer for the job.

Being new at c++ I'm unsure what the correct pattern is for this type of flow.

utahwithak
  • 6,235
  • 2
  • 40
  • 62

4 Answers4

1

C++ treats const very strictly. Copying such a struct will be painful if possible at all (without UB). Perhaps what you are really looking for is "readonly" instead? In that case you can make fields private with public getters/setters:

struct Foo {
    public:
        Foo(): id(-1), value(-1) {}
        Foo(int id): id(id), value(0) {}

        int getId() const {
            return id;
        }

        int getValue() const {
            return value;
        }

        void setValue(int newValue) {
            value = newValue;
        }

    private:
        int id;
        int value;
};
freakish
  • 54,167
  • 9
  • 132
  • 169
  • I think that the semantics between swift and c++ are clear where in swift it is an array "object" full of values and those values are all distinct and you're setting something on the array whereas in c++ it is just those values and you're not setting things in some array but adjusting the value there already. – utahwithak Aug 26 '20 at 15:15
1

You can never change a const value after it's been initialized.

From the name id it sounds like move semantics is what you need. Instead of having two valid objects with the same id, you can make it possible to move the data between objects, but not to copy it.

Example:

#include <iostream>
#include <utility>

struct Foo {
    Foo() : id(-1), value(-1) {}                      // default - "invalid" values

    Foo(int id, int value) : id(id), value(value) {} // constructor
    explicit Foo(int id) : Foo(id, 0) {}             // converting ctor, delegating 

    Foo(const Foo&) = delete;                        // no copy construction
    Foo(Foo&& rhs) :                                 // move construction ok
        id(std::exchange(rhs.id, -1)),               // take id, give -1 back
        value(std::exchange(rhs.value, -1))          // take value, give -1 back
    {}
    Foo& operator=(const Foo&) = delete;             // no copy assignment
    Foo& operator=(Foo&& rhs) {                      // move assignment ok
        // give the "moved from" element our id and value and
        // take id and value from the "moved from" element
        std::swap(id, rhs.id);
        std::swap(value, rhs.value);
        return *this;
    }

    int Id() const { return id; }        // only const access
    
    int& Value() { return value; }       // non-const access in non-const context
    int Value() const { return value; }  // const access in const context

private:
    int id;
    int value;
};

int main() {
    Foo foos[2];
    foos[0] = Foo(1234);     // The temporary Foo will have (-1, -1) when it's destroyed
    for(auto& f : foos)
        std::cout << f.Id() << ' ' << f.Value() << '\n';
}

Output:

1234 0
-1 -1
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

In C++, an object can be initialized only once. Your constant member is initialized via the default constructor when you declare the array. Then, you attempt to change that value via assignment

foos[0] = Foo(1234);

This fails, and righly so. One thing you could do is to have a container of not Foos, but some sort of pointers to foo. Thus, you will have allocated the memory for the pointers but will create the actual objects only when you can. For example:

Foo* foos[10]; //here you have only the pointers
foos[0] = new Foo(1234); //OK, the object is inialized here

You may consider using smart pointers such as std::unique_ptr instead of raw pointers here.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
0

Times, and C++ standards have changed!

You can now define your own copy-assignment operator for classes that contain const member objects without undefined behavior as of c++20.

This was undefined behavior prior to c++ and remains so for complete const objects but not non-const objects with const members.

https://stackoverflow.com/a/71848927/5282154

doug
  • 3,840
  • 1
  • 14
  • 18