Search code examples
c++structconstantscopy-constructordefault-constructor

Default Copy Operations for Structs with Constant Members


I have a Texture struct that I am using to hold the width, height, and id number of a texture. I also have a Loader class with many static functions dedicated to loading content, such as textures. The problem arises when I try to declare an uninitialized Texture, and then initialize it later. Here is the code in Texture.h:

namespace bronze {

struct Texture {
    const unsigned int id;
    const float width;
    const float height;

    Texture() = default;
    Texture(Texture&&) = default;
    Texture& operator=(Texture&&) = default;
};

}

In Loader.cpp

Texture(const std::string& file, float scale) {
    unsigned int id;
    float width, height;

    loadTextureUsingExternalLibrary(&id, &width, &height);
    doThingsWithTexture(id);

    return Texture{id, width, height}
}

And then in main.cpp:

#include "Loader.h"
#include "Texture.h"

using namespace bronze;

Texture tex;

int main() {
    tex = Loader::loadTexture("asdf.png", 2.f);
    drawTextureOnTheWindowSomehow(tex);
    return 0;
}

Here are the (shortened, of course) errors I am getting (MinGW is my compiler):

error: use of deleted function 'bronze::Texture::Texture()'
Texture tex;

note: 'bronze::Texture::Texture()' is implicitly deleted because the default
definition would be ill-formed:
Texture() = default;

...complains about each member being uninitialized

error: use of deleted function 'bronze::Texture& bronze::Texture::operator=
Bronze::Texture&&)'
tex = Loader::loadTexture("asdf.png", 2.f);

note: 'bronze::Texture& bronze::Texture::operator=(bronze::Texture&&)' is
implicitly deleted because the default definition would be ill-formed:
Texture& operator=(Texture&&) = default;

...complains for each struct member that "non-static const member can't use
default assignment operator"

I have been Googling around for a while now, and cannot find anything. Perhaps it is that I do not know what to Google, I do not know. Help is appreciated!


Solution

  • A few parts to this

    1.The default constructor doesn't work because you can't have an uninitialized const object (even primitives). For your simple case you probably just want them to be value initialized and this can be achieved easily enough:

    struct Texture {
        const unsigned int id{};
        const float width{};
        const float height{};
        //...
    };
    

    2.You can't use the implicitly generated operator= for an object with const data members because that would require assigning to const objects.

    struct A { const int i{}; };
    A a1;
    A a2(a1); // fine
    a1 = a2; // not fine. can't do a1.i = a2.i since a1.i is const!
    

    If you want to be able to assign, you'll need to use non-const data members. You cannot use the implicit operator= if you have const members* You could const_cast but that'd lead to undefined behavior and is a horrible idea (just saying it before someone mentions it in the comments).

    You aren't just declaring tex, you are defining it. The point of definition requires being initialized. Trying to assign later is not initializing it.

    Don't use global variables.

    *unless those const members have an operator=(..) const but that'd be pretty weird