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!
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
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;
}
Related
I have a class that takes in a boolean called fixed as an argument. I want it to initialize the data member position as const if fixed is true. Is this possible at all?
class PhysicsVertex
{
public:
PhysicsVertex(olc::vf2d position, const bool fixed = false) :
position(position), fixed(fixed)
{
}
olc::vf2d position; //make this const if fixed is true.
const bool fixed = false;
};
int main()
{
PhysicsVertex v1{ {0, 0}, true }; //initialize v1.position as const
PhysicsVertex v1{ {0, 0} }; //initialize v1.position as non-const
}
If your information about fixed parameter is needed at compile time then you can use templates
#include <type_traits>
struct vf2d
{
};
template <bool fixed>
class PhysicsVertex
{
public:
PhysicsVertex() :
position()
{
}
typename std::conditional<fixed, const vf2d, vf2d>::type position;
};
void foo()
{
auto vertex = PhysicsVertex<true>();
// vertex.position = vf2d(); - compile time error, you cannot change const member
auto vertex2 = PhysicsVertex<false>();
vertex2.position = vf2d(); // It works fine, you can change non const member
}
const class members are problematic, because they inhibit a whole bunch of compiler generated methods. For example PhysicsVertex cannot be copied. Often the better alternative is to not make it const but instead provide no means to modify it from outside the class:
class foo {
int x = 42;
public:
int get() { return x; }
};
The member is not modifyable.
Now to your quesiton. You cannot make it const or non-const based on a runtime value (at least not easily), but following the suggestion above you can conditionally allow to modify it or not:
class foo {
int x = 42;
bool modifyable = false;
public:
foo(bool modifyable) : modifyable(modifyable) {}
void set(int y) {
if (!modifyable) throw "cannot modify";
x = y;
}
};
You should reconsider whether you can already decide to make it const or non-const at compile time, because then you need not rely on runtime checks and excpetions but can use the approach described in this answer.
I'm trying to make a program that deals with images, in which I have an image object and an image_view object that references a rectangle region in the image:
class image_view;
class image
{
public:
image(int width, int height);
operator image_view() const;
private:
int m_width;
int m_height;
std::vector<pixel> m_pixels;
};
class image_view
{
public:
image_view(const image& ref, point origin, int width, int height);
image_view(image_view view, point origin, int width, int height);
private:
const image& m_ref;
point m_origin;
int m_width;
int m_height;
};
However, when I tried to copy an image_view, the compiler told me that the operator= member function had been deleted because of the non-static member reference. I naively tried to make my own member function with m_ref = other.m_ref but it didn't work since m_ref is const.
I considered using a smart pointer instead of a reference, but I didn't find a way of making a smart pointer to an already existing object.
One solution I found is the following:
image_view& image_view::operator= (const image_view& other)
{
*this = image_view(other);
return *this;
}
This compiles, but is it a good idea? (I might be wrong, but I feel bad about assigning stuff to *this.) If I do that, does that mean I have to define a destructor (even though I'm not allocating anything), move constructor and move assignment operator to satisfy the rule of 5?
If you want to express a non-owning, non-null wrapper that rebinds on assignment, the easiest way to do that is to use std::reference_wrapper:
class image_view
{
public:
image_view(const image& ref, point origin, int width, int height);
image_view(image_view view, point origin, int width, int height);
private:
std::reference_wrapper<image const> m_ref;
point m_origin;
int m_width;
int m_height;
};
The default copy constructor and assignment operator will do the right thing.
I have a C++ class with two constructors (a default one and another with arguments). In order to reuse code, I avoided initializing class members at the constructor level, and I'm doing it in an Initialize method instead, which I am calling from both constructors. This way, I was hopping to minimize code lines and repeated code:
Location::Location(){
double pos[POSITION_SIZE] = {0};
this->Initialize(const_cast<char*>(""), const_cast<char*>(""), pos);
}
Location::Location(char *id, char *code, double pos[POSITION_SIZE]){
this->Initialize(id, code, pos);
}
void Location::Initialize(char *id, char *code, double pos[POSITION_SIZE]){
strcpy(this->ID, id);
strcpy(this->code, code);
this->position[0] = pos[0];
this->position[1] = pos[1];
this->position[2] = pos[2];
this->attribute1 = 0;
this->attribute2 = 0;
}
header:
class Location{
public:
Location();
Location(char *id, char *code, double pos[POSITION_SIZE]);
private:
// This method initializes the location attributes given as parameters
void Initialize(char *id, char *code, double pos[POSITION_SIZE]);
// Name/identifier of the location
char ID[ID_LENGTH];
// FIR identifier
char code[ID_LENGTH];
// Location's coordinates (lat, lon, alt)
double position[POSITION_SIZE];
// Attribute 1
double attribute1;
// Attribute 2
double attribute2;
};
I know that using initialize methods is a bad praxis when used because old school coding style or avoiding the usage of exceptions at constructor for example. But my goal here was reducing code, so unless some guru of stackoverflow says the opposite, I think it is not wrong (but I'm here to learn, so please destroy all my convictions).
The problem is that I'm getting a warning for not initializing class members within the cosntructor. The compiler doesn't like them to get initialized at the Initialize method. So, any way of making the compiler happy? Should I forget aboput Initialize method usage?
I would use constructor delegation, something like:
#include <iostream>
using namespace std;
class foo
{
public:
foo()
: foo(1, "2", 3.) // delegate to the other constructor with defaults...
{ }
foo(int a, std::string b, double c)
: _a(a), _b(b), _c(c)
{ }
private:
int _a;
std::string _b;
double _c;
};
int main() {
foo f1{};
foo f2{1, "3", 4.};
return 0;
}
With the caveat that you can use atleast c++11...
for some reason I can´t achieve this.
Line::Line(const Pixel &aStart, const Pixel &aEnd){
start = aStart;
end = aEnd;
}
the Line class:
class Line : public Vertex{
public:
Line(const Pixel &start, const Pixel &end);
Pixel getStart();
Pixel getEnd();
private:
Pixel start;
Pixel end;
};
g++ tells me
error: no matching function for call to ‘Pixel::Pixel()’
note: candidates are:
- Pixel::Pixel(int, int, int, int, int)
- Pixel::Pixel(int, int)
- Pixel::Pixel(const Pixel&)//not implemented by me, some sort of default constructor?
I thought actually Im using the last constructor, but something doesnt work. Any help much appreciated.
EDIT: The Pixel Class:
class Pixel{
public:
Pixel(int x, int y);
Pixel(int red, int green, int blue, int x, int y);
void setRed(int red);
void setGreen(int green);
void setBlue(int blue);
void setColor(int r, int g, int b);
int getRed();
int getGreen();
int getBlue();
bool isChanged();
int getX();
int getY();
private:
int red;
int green;
int blue;
int x;
int y;
bool hasBeenChanged;
};
The Line members of type Pixel, start and end, will be default constructed before the constructor of Line runs and sets them to the objects passed in as arguments. It seems that your Pixel class doesn't have a default constructor (because giving any user-defined constructor will stop the compiler from giving you an implicit default constructor). You need to make use of a constructor member initialization list:
Line::Line(const Pixel &aStart, const Pixel &aEnd)
: start(aStart), end(aEnd)
{ }
This initialises start and end with aStart and aEnd respectively, bypassing the default construction that would normally occur.
Because you declare this
Pixel::Pixel(int, int, int, int, int)
Pixel::Pixel(int, int)
the compiler doesn't generate a default constrcutor, or a constructor exists with arguements having default values.
Pixel start;
Pixel end;
These require a default constructor. It doesn't find one, hence the compiler complains. You current code requires the default constructors.
And as a good practice use the initialization list, which wouldn't ask the arguements to default constructed.
Line::Line(const Pixel &aStart, const Pixel &aEnd):start(aStart), end(aEnd){}
I'm trying to compile class A, which has a member of class B, where class B has no default constructor and its only constructor requires multiple arguments. Simple, right? Apparently not...
Class A:
class SessionMediator
{
public:
SessionMediator()
: map_(16,100,100)
{}
Tilemap map_, background_, foreground_;
};
Class B:
struct Tile2D;
class Tilemap
{
public:
Tilemap(const unsigned int tile_size, const unsigned int width,
const unsigned int height)
: tiles_(NULL), tile_size_(tile_size)
{
Resize(width, height);
}
inline void Resize(const unsigned int width, const unsigned int height)
{ /* Allocate tiles & assign to width_, height_... */ }
unsigned int tile_size_, width_, height_;
Tile2D* tiles_;
};
I am instantiating SessionMediator like so:
int main(int argc, char** argv)
{
SessionMediator session;
return 0;
}
This is the error I am getting. I'm compiling in XCode on Mac OS 10.5.8 and the compiler is g++:
session_mediator.h: In constructor 'SessionMediator::SessionMediator()':
session_mediator.h:19: error: no matching function for call to 'Tilemap::Tilemap()'
tilemap.h:31: note: candidates are: Tilemap::Tilemap(unsigned int, unsigned int, unsigned int)
tilemap.h:26: note: Tilemap::Tilemap(const Tilemap&)
session_mediator.h:19: error: no matching function for call to 'Tilemap::Tilemap()'
tilemap.h:31: note: candidates are: Tilemap::Tilemap(unsigned int, unsigned int, unsigned int)
tilemap.h:26: note: Tilemap::Tilemap(const Tilemap&)
(Duplicate of above here)
Build failed (2 errors)
I wrote a short compilable example doing basically the same thing, to try to figure out what exactly I was doing wrong, which compiles just fine with no errors in g++:
class A
{
public:
A(int x, int y, int z)
: x_(x), y_(y), z_(z)
{}
int x_, y_, z_;
};
class B
{
public:
B()
: m_a(1,2,3)
{}
A m_a;
};
int main(int argc, char **argv)
{
B test;
return 0;
}
Why does it fail in the first example? The 3 arg constructor for Tilemap (in Ex#1) is being called in the same way that the 3 arg constructor for A is being called (in Ex#2).
The code seems pretty much identical to me in the two examples.
As I was trying to simplify my example a bit, I accidentally left out two important things: other data members in the SessionMediator class.
The problem was that I had two additional members of the Tilemap class ("background_" and "foreground_"), which weren't being initialized in the constructor initialization list like the first member "map_".
The constructor should be changed to this:
SessionMediator()
: map_(16,100,100), background_(1,1,1), foreground_(1,1,1)
{}
I apologize for any time wasted over this question; it turned out to be something much simpler. Hopefully someone else will see this question and realize the mistake they are making.
The only thing I can think of is if you are using the copy constructor:
SessionMediator a = b; or SessionMediator a (b);
You might get in the situation where the default copy constructor of SessionMediator would try to use the default constructor of Tilemap which will cause the error you have.
Try to put map_(16u,100u,100u) in the SessionMediator constructor call to make the constants unsigned. This is the only thing that comes to mind now :-).
This compiles fine for me:
class Tilemap
{
public:
Tilemap(const unsigned int tile_size, const unsigned int width,
const unsigned int height)
{
}
};
class SessionMediator
{
public:
SessionMediator(): map_(16u,100u,100u){}
Tilemap map_;
};
Well, when you do:
Tilemap map_;
You're calling the default ctor - but you don't have one defined, that's the error message.
On the extra:
Tilemap::Tilemap(const Tilemap&)
C++ generates a ctor that takes a reference for you. So the valid matches are (1) the one you defined which takes 3 args and (2) the auto-generated one that takes the const ref.