Storing a reference inside a class - c++

I want to re-define [] operator for a vector. I thought the simplest way to achieve this is to create my own class, say something like this (this is a simplified example):
class MyVec
{
private:
std::vector<double> x; // I don't need a copy in here, it could be a reference
public:
MyVec(const std::vector<double>& y) { x = y; }
double operator[](int i) { return x[i-1]; }
};
Now the problem is that I basically do not need to copy data to x, I could store a reference to it, but as far as I know, I cannot use uninitialized references in class. Nonetheless, maybe there is some kind of workaround for this to work without copying the data?

class construction does not allow for this
It does, you just need to use the member initializer list. Using it is the recommended way of initializing class members from the constructor - you should not use the constructor body for that.
class MyVec
{
private:
const std::vector<double>& x;
public:
MyVec(const std::vector<double>& y) : x{y} { }
double operator[](int i) { return x[i-1]; }
};
wandbox example
Note that if you take a const& in the constructor, you need to store a const&, otherwise you lose the const-ness of passed constructor argument.

For the implmentation of the constructor like MyVec(const std::vector<double>& y) { x = y; }, member x will be default constructed and then assigned by y in the constructor's body. But reference members can't be default constructed.
So if you declare the member as reference you have to initialize them by member initializer list:
For members that cannot be default-initialized, such as members of reference and const-qualified types, member initializers must be specified.
e.g.
class MyVec
{
private:
const std::vector<double>& x; // it's a reference (to const)
public:
MyVec(const std::vector<double>& y) : x(y) {}
double operator[](int i) { return x[i-1]; }
};
Note that for this case the member has to be declared as reference to const, because the parameter type of the constructor is reference to const.

Related

Error while passing an object by reference to another class' constructor

I have two classes one of them has an object of another class as a data member and it's constructor accepts the class object to initialize the data member object.
class x{
public:
x(int a, int b)
{ cout << a << b;}
};
class y{
x temp;
y(x& o){ this-> temp = o;}
};
But compiler shows an error in y::y(x&): no matching function to call x::x()
I am using codeblocks 16.01
You have defined the constructor:
x(int a, int b)
in x. This means that the compiler will no longer define any constructors for you, this includes the x() constructor. So you can only construct x with x(int, int). Here in your code:
x temp;
y(x& o) { // < No initializer list
You attempt to default construct x, but x has no default constructor! Either define one, or construct x in the initializer list with the constructor you have provided.
For example:
y(x& o) : x(0, 0) {
But you will create your object then you will use the implicitly defined copy-assignment operator to assign it, which is a bit of a waste of time. You can actually solve all these problems by using the copy-constructor:
class x{
...
x(const x &copy) { // Define a copy constructor or just use
// the implicitly defined one.
Then in y, just use it in y's initializater list:
x temp;
y(x& o) : temp(o) {}
y(x& o){ this-> temp = o; }
is not very C++ idiomatic.
I. As a rule, you should avoid requiring more access rights than needed. Here, you're likely to not mutate the construction argument, so you don't need to pass it by a mutable reference:
y(x const &o);
II. Member initialization is done very differently in C++:
y(x const &o): temp(o) {}
When you write
y(x const &o) { temp = o; } // please avoid writing `this->'
then what happens is: first, temp is constructed and default-initialized (prior to the opening brace); then, inside the braces, temp is already a valid object, so what follows is a copy-assignment. In your case, x is not default-constructible, so compilation fails.

move assignment to object with const value

I have a struct like this:
struct OBJ {
int x;
const int y;
OBJ& operator=(OBJ &&oth)
{
y = oth.y; // this is disallowed
return *this;
}
}
And an example code
void func() {
static OBJ obj;
OBJ other; // random values
if(conditon)
obj = std::move(other); //move
}
I understand this as obj is Non const OBJ with const member y. I can't change just y but I should be able to change whole object (call destructor and constructor). Is this possible or the only proper solution is to remove my const before y, and remember to don't change by accident?
I need to store my static obj between func call but if condition is true i want to move other object in place of this static object.
I would suggest moving to std::unique_ptr:
void func() {
static std::unique_ptr<OBJ> obj = std::make_unique<OBJ>();
std::unique_ptr<OBJ> other = std::make_unique<OBJ>(); // random values
if(condition)
obj = std::move(other); //move
}
This should be your choice in many cases where there is a need to move something that cannot be moved, to hold an unknown polymorphic type or any other case where you cannot deal with the actual type.
You're doing constructors wrong. Constructors should initialize, not assign:
OBJ(OBJ &&oth) : y(oth.y) {}
// ^^^^^^^^^^
Also, constructors cannot return *this, since they have no return type.
An assignment operator for your class doesn't make sense since the class has unassignable members (namely constants). (You could of course write a custom assignment that doesn't modify the const member, but then you'd have a truly weird class that has extremely surprising behaviour.)
What about writing the move-assignment operator this way:
OBJ& operator=(OBJ&& other) {
this->~OBJ();
new(this) OBJ(other.x, other.y);
return *this;
}
You would also need a constructor:
OBJ(const int x, const int y)
: x(x), y(y)
{
}

How to copy (or swap) objects of a type that contains members that are references or const?

The problem I am trying to address arises with making containers such as an std::vector of objects that contain reference and const data members:
struct Foo;
struct Bar {
Bar (Foo & foo, int num) : foo_reference(foo), number(num) {}
private:
Foo & foo_reference;
const int number;
// Mutable member data elided
};
struct Baz {
std::vector<Bar> bar_vector;
};
This won't work as-is because the default assignment operator for class Foo can't be built due to the reference member foo_reference and const member number.
One solution is to change that foo_reference to a pointer and get rid of the const keyword. This however loses the advantages of references over pointers, and that const member really should be const. They are private members, so the only thing that can do harm is my own code, but I have shot myself in the foot (or higher) with my own code.
I've seen solutions to this problem on the web in the form of swap methods that appear to be chock full of undefined behavior based on the wonders of reinterpret_cast and const_cast. It happens that those techniques do appear to work on my computer. Today. With one particular version of one particular compiler. Tomorrow, or with a different compiler? Who knows. I am not going to use a solution that relies on undefined behavior.
Related answers on stackoverflow:
Does it make sense to implement the copy-assignment operator in a class with all const data members?
The first answer has the amusing line "If that one is immutable, your screwed."
Swap method with const members
The first answer doesn't really apply here, and the second is a bit of a kludge.
So is there a way to write a swap method / copy constructor for such a class that does not invoke undefined behavior, or am I just screwed?
Edit
Just to make it clear, I already am quite aware of this solution:
struct Bar {
Bar (Foo & foo, int num) : foo_ptr(&foo), number(num) {}
private:
Foo * foo_ptr;
int number;
// Mutable member data elided
};
This explicitly eliminates the constness of number and the eliminates the implied constness of foo_reference. This is not the solution I am after. If this is the only non-UB solution, so be it. I am also quite aware of this solution:
void swap (Bar & first, Bar & second) {
char temp[sizeof(Bar)];
std::memcpy (temp, &first, sizeof(Bar));
std::memcpy (&first, &second, sizeof(Bar));
std::memcpy (&second, temp, sizeof(Bar));
}
and then writing the assignment operator using copy-and-swap. This gets around the reference and const problems, but is it UB? (At least it doesn't use reinterpret_cast and const_cast.) Some of the elided mutable data are objects that contain std::vectors, so I don't know if a shallow copy like this will work here.
You can't reseat the reference. Just store the member as a pointer, as it is done in all other libraries with assignable classes.
If you want to protect yourself from yourself, move the int and the pointer to the private section of a base class. Add protected functions to only expose the int member for reading and a reference to the pointer member (e.g to prevent yourself from treating the member as an array).
class BarBase
{
Foo* foo;
int number;
protected:
BarBase(Foo& f, int num): foo(&f), number(num) {}
int get_number() const { return number; }
Foo& get_foo() { return *foo; }
const Foo& get_foo() const { return *foo; }
};
struct Bar : private BarBase {
Bar (Foo & foo, int num) : BarBase(foo, num) {}
// Mutable member data elided
};
(BTW, it doesn't have to be a base class. Could also be a member, with public accessors.)
If you implement this with move operators there is a way:
Bar & Bar :: operator = (Bar && source) {
this -> ~ Bar ();
new (this) Bar (std :: move (source));
return *this;
}
You shouldn't really use this trick with copy constructors because they can often throw and then this isn't safe. Move constructors should never ever throw, so this should be OK.
std::vector and other containers now exploit move operations wherever possible, so resize and sort and so on will be OK.
This approach will let you keep const and reference members but you still can't copy the object. To do that, you would have to use non-const and pointer members.
And by the way, you should never use memcpy like that for non-POD types.
Edit
A response to the Undefined Behaviour complaint.
The problem case seems to be
struct X {
const int & member;
X & operator = (X &&) { ... as above ... }
...
};
X x;
const int & foo = x.member;
X = std :: move (some_other_X);
// foo is no longer valid
True it is undefined behaviour if you continue to use foo. To me this is the same as
X * x = new X ();
const int & foo = x.member;
delete x;
in which it is quite clear that using foo is invalid.
Perhaps a naive read of the X::operator=(X&&) would lead you to think that perhaps foo is still valid after a move, a bit like this
const int & (X::*ptr) = &X::member;
X x;
// x.*ptr is x.member
X = std :: move (some_other_X);
// x.*ptr is STILL x.member
The member pointer ptr survives the move of x but foo does not.
that const member really should be const
Well, then you can't reassign the object, can you? Because that would change the value of something that you've just said really should not change: before the assigment foo.x is 1 and bar.x is 2, and you do foo = bar, then if foo.x "really should be const" then what's supposed to happen? You've told it to modify foo.x, which really should not be modified.
An element of a vector is just like foo, it's an object that the container sometimes modifies.
Pimpl might be the way to go here. Dynamically allocate an object (the "impl") containing all your data members, including const ones and references. Store a pointer to that object (the "p") in your object that goes in the vector. Then swap is trivial (swap the pointers), as is move assignment, and copy assignment can be implemented by constructing a new impl and deleting the old one.
Then, any operations on the Impl preserve the const-ness and un-reseatable-ness of your data members, but a small number of lifecycle-related operations can act directly on the P.
This however loses the advantages of references over pointers
There is no advantage. Pointers and reference are different, but none is the better one. You use a reference to ensure that there is a valid instance and a pointer if passing a nullptr is valid. In your example you could pass a reference and store a pointer
struct Bar {
Bar (Foo & foo) : foo_reference(&foo) {}
private:
Foo * foo_reference;
};
You can compose your class of members that take care of those restrictions but are assignable themselves.
#include <functional>
template <class T>
class readonly_wrapper
{
T value;
public:
explicit readonly_wrapper(const T& t): value(t) {}
const T& get() const { return value; }
operator const T& () const { return value; }
};
struct Foo{};
struct Bar {
Bar (Foo & foo, int num) : foo_reference(foo), number(num) {}
private:
std::reference_wrapper<Foo> foo_reference; //C++11, Boost has one too
readonly_wrapper<int> number;
// Mutable member data elided
};
#include <vector>
int main()
{
std::vector<Bar> bar_vector;
Foo foo;
bar_vector.push_back(Bar(foo, 10));
};
I know this is a relatively old question to dig out, but I have needed something similar recently, and it made me wonder if this is possible to implement using contemporary C++, e.g. C++17.
I've seen this blog post by Jonathan Boccara
and fiddled with it a little, i.e. by adding implicit conversion operators and this is what I have gotten:
#include <optional>
#include <utility>
#include <iostream>
#include <functional>
template <typename T>
class assignable
{
public:
assignable& operator=(const assignable& rhs)
{
mHolder.emplace(*rhs.mHolder);
return *this;
}
assignable& operator=(assignable&&) = default;
assignable(const assignable&) = default;
assignable(assignable&&) = default;
assignable(const T& val)
: mHolder(val)
{}
assignable(T&& val)
: mHolder(std::move(val))
{}
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return (*mHolder)(std::forward<Args>(args)...);
}
operator T&() {return *mHolder;}
operator const T&() const {return *mHolder;}
private:
std::optional<T> mHolder;
};
template <typename T>
class assignable<T&>
{
public:
explicit assignable(T& val)
: mHolder(val)
{}
operator T&() {return mHolder;}
operator const T&() const {return mHolder;}
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return mHolder(std::forward<Args>(args)...);
}
private:
std::reference_wrapper<T> mHolder;
};
The part some may find controversial is the behaviour of the assignment operator for reference types, but the discussion about rebinding the reference vs the underlying object has been going on there for a while already I think, so I just did according to my taste.
Live demo:
https://godbolt.org/z/WjW7s34jn

Confusion with member copying of class

After studying the member copying and the assignment operator in C++,
and looking at " member copying of class " which explains the conditions where default assignment operator cannot be generated. I am not very clear about the concepts as the following example I tried actually works on g++4.5
#include<iostream>
using namespace std;
class Y{
int& x;
const int cx;
public:
Y(int v1,int v2)
:x(v1),cx(v2)
{}
int getx(){return x;}
int getcx(){return cx;}
};
int main()
{
int a = 10;
Y y1(a,a);
Y y2 = y1;//assignment
cout<<y1.getx()<<" "<<y1.getcx();
return 0;
}
So where am I not getting the concepts. Please suggest other examples (if possible) so that I can understand better.
Y y2 = y1; is not an assignment. It's a copy constructor call. If you declare and initialize a variable on the same line, a one parameter constructor is called with the right hand side of the equals sign as the parameter. There's nothing about Y that prevents the default copy constructor from being instantiated (and called).
Try the following:
Y y1(10, 10);
Y y2(11, 11);
y2 = y1;
This should fail, although I can't test it right now.
class Y{
int& x;
public:
Y(int v1,int v2)
:x(v1),cx(v2)
{} // v1 ceases to exist from this point
};
x is a reference variable to an int. Now you are initializing it to v1, which means x is an alias to v1 itself. The scope of v1 is in the constructor alone. With that said -
Y y2 = y1;//assignment => Not assignment. It is initialization.
is equivalent to
Y y2(y1); // compiler is looking for the overloaded constructor ( ie. copy constructor in this case ).
class Y{
public:
Y ( const Y & other ); // copy constructor
// ...
};
#include<iostream>
using namespace std;
class Y{
int& x;
const int cx;
public:
Y(int v1,int v2)
:x(v1),cx(v2)
{}
int getx(){return x;}
int getcx(){return cx;}
};
int main()
{
int a = 10;
Y y1(a,a);
Y y2 = y1;//NOT assignment. Object is yet to be constructed, so calls copy c'tor
y2 = y1; // assignment operator is called
cout<<y1.getx()<<" "<<y1.getcx();
return 0;
}
/*
D:\Workspaces\CodeBlocks\Test\main.cpp||In member function 'Y& Y::operator=(const Y&)':|
D:\Workspaces\CodeBlocks\Test\main.cpp|4|error: non-static reference member 'int& Y::x', can't use default assignment operator|
D:\Workspaces\CodeBlocks\Test\main.cpp|4|error: non-static const member 'const int Y::cx', can't use default assignment operator|
D:\Workspaces\CodeBlocks\Test\main.cpp||In function 'int main()':|
D:\Workspaces\CodeBlocks\Test\main.cpp|20|note: synthesized method 'Y& Y::operator=(const Y&)' first required here |
||=== Build finished: 3 errors, 0 warnings ===|
*/
Your class contains members which cannot be default-constructed or assigned, namely:
References
Constants
Therefore, no default constructor or assignment operator can be implied for your class. For example, you have to write your own constructor:
class Foo
{
const int a;
int & b;
public:
Foo(int val, int & modify_me) :
a(val) , // initialize the constant
b(modify_me) // bind the reference
{ }
};
It is clear that you cannot default-construct Foo (i.e. Foo x;). It is also clear that you cannot reassign objects of class Foo (i.e. x = y;), because you cannot reassign references or constants.
By giving your class a reference or a constant member, you actually confer reference or constant semantics on the class itself, if you will, so this should be a fairly immediate logical consequence. For example, reassignment probably doesn't even make sense semantically, because your class is supposed to embody a constant concept.
However, note that you can make copies of your class: That's because you can make "copies" of references (i.e. furhter aliases), and copies of constants. Therefore, a copy constructor is available implicitly, by simply applying copy-construction member-by member. So you can say:
int n;
Foo x(15, n);
Foo y(x);
Foo z = x; // these two are identical!
This results in two further objects y and z which have y.a == 15 and z.a == 15, and y.b and z.b are all references to n. (Don't be confused by the two alternative syntaxes at the end; both invoke the copy constructor.)

Const Member function Vs Const Friend Function

How this code modifies a value in a const function:
#include<iostream>
using namespace std;
class Y;
class X{
public:
void access(Y &y) const;
};
class Y{
int d;
public:
friend void X::access(Y &y) const;
Y(){d=0;}
};
void X::access(Y &y) const
{
cout<<"Y is "<<y.d<<endl;
y.d=1;
cout<<"Y is "<<y.d<<endl;
}
int main() {
X x;
Y y;
x.access(y);
}
And this code gives an error:
#include<iostream>
using namespace std;
class Y{
int d;
public:
void set() const{d=0;}
};
int main() {
Y y1;
return 0;
}
Because in your first example:
void X::access(Y &y) const;
the const tells the compiler that the function won't modify the class X object that the implicit this parameter points to. The class Y object that's passed as a reference isn't const.
In the second example, the set() function is a member of class Y:
void set() const
and it's declared such that the implicit this parameter pointing to the Y object is a const pointer (so that object can't be modified).
If you want X::access() to not be permitted to modify the Y object passed to it, change the declaration to:
void X::access(Y const& y) const;
Because inside a const function, only *this is const, not the parameters passed to the function or anything else.
In the first example, the const-qualifier on X::access(Y &y) const means that the X object on which it is called cannot be modified. Since the Y parameter is taken by non-const reference, it can be modified from within the function.
In the second example, the const-qualifier on Y::set() const means that the Y object on which it is called cannot be modified, hence why you cannot modify the non-mutable member variable d.
a member function being const only promises not to change any members of the class it belongs to (execpt if they are declared mutable).
In the first example, Y.d is being modified in method X::access. The method does not modify any members of X itself so is perfectly sane.
In the second example however, the Y::set() method is declared const so it cannot change Y::d.
Next time, please post the code here instead of providing a link.