2

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!

Sus Among Us
  • 103
  • 2
  • 11
  • `Texture(const std::string& file, float scale)` should be `Texture loadTexture(const std::string& file, float scale) {` ? – M.M Mar 31 '16 at 01:58

2 Answers2

2

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

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • Thanks! One question though, does the fact that I can't use an implicit `operator=` mean that there is no way for me to write `tex = Loader::loadTexture("asdf.png", 2.f);` without using non-const members? – Sus Among Us Mar 31 '16 at 00:55
  • @RyanHaining You could still define an explicit `operator=` – wally Mar 31 '16 at 02:05
  • 1
    @Naerion my advice would be to not use `const` members , they're more trouble than they are worth. – M.M Mar 31 '16 at 02:05
  • @flatmouse Which wouldn't be much use since it would not be able to perform the logical function that assignment does – M.M Mar 31 '16 at 02:05
  • @M.M Are you sure? What if you had both types of members? – wally Mar 31 '16 at 02:06
  • @flatmouse assigning one texture to another should update the target's width and height, surely – M.M Mar 31 '16 at 02:07
  • Okay cool thanks everyone. It works fine; and I've come away with a better understanding of C++. I just used non-const members with some get functions. Easy enough. – Sus Among Us Mar 31 '16 at 02:09
  • @flatmouse yeah I'm not seeing what you mean, example? – Ryan Haining Mar 31 '16 at 03:57
  • @flatmouse "*both types of members*" do you mean some const members and some non-const members? Do you mean both types of member functions `operator=(const T&)` and `operator=(const T&) const`? Do you mean defaulted and explicit member functions for construction and assignment? – Ryan Haining Mar 31 '16 at 17:04
  • @RyanHaining Some const members and some non-const members. – wally Mar 31 '16 at 18:33
  • @flatmouse I can imagine that there are situations but it wouldn't make sense for anything that exposes its members. – Ryan Haining Mar 31 '16 at 20:00
1

Texture is an aggregate with const members. You must provide the values during initialization.

When you return with return Texture{id, width, height} it does the aggregate initialization for you, but if you try to construct Texture the constructor could never work without initializing the const members.

So you might rather use:

Texture(unsigned int id, float width, float height) : id{id}, width{width}, height{height} {};

Also note that you may not be able to use the implicitly deleted operator=, but you may define your own.

The following program works fine with the assignment operator.

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

    Texture(unsigned int id,float width,float height,int other) : id{id},width{width},height{height},other{other} {};
    Texture(const Texture & rhs) : id{rhs.id},width{rhs.width},height{rhs.height} {};
    Texture& operator=(const Texture& rhs)
    {
        other = rhs.other;
        return *this;
    };
};

int main()
{
    Texture t0{1,2,3,0};
    auto t1 = Texture(3,3,3,1); 
    Texture t2 = t0; // t2's constants are initialized to be the same as t0's
    t2 = t1; // only the non const member data is copied
    return 0;
}
wally
  • 10,717
  • 5
  • 39
  • 72
  • Thank you! I had actually never heard of the term aggregate before. I understand more of what is going on now. – Sus Among Us Mar 31 '16 at 01:03
  • You're welcome. I wrote an answer [here](http://stackoverflow.com/a/36270998/1460794) about initialization in general. – wally Mar 31 '16 at 01:39