Keep pointers between class members stable when moving - c++

Suppose a class C has two members, M1 and M2, where M1 holds a pointer P that refers to M2.
For every instance X of C the following invariant should hold: X.M1.P points to X.M2.
Is there an idiomatic way to implement this behaviour in C++ without having to implement a move constructor and assignment operator?
I'm asking because it seems to me that conceptually the pointer P should be relative to M1. Non-default move constructors and assignment operators seem like lots of work (especially if there are a few members) for this simple concept.
My specific use-case is similar to the example below (where set is M1, and less is M2). Perhaps my fundamental approach is flawed — is there a better way than having a pointer to Relation in Less?
using Relation = std::vector<std::vector<bool>>;
class C {
public:
C() { /* less = ...; */ }
C(C&&) = default;
C& operator=(C&&) = default;
private:
struct Less {
explicit Less(const Relation* r) : r(r) {}
bool operator()(int i, int j) { return (*r)[i][j]; }
const Relation* r;
};
Relation less;
std::set<int, Less> set{Less(&less)};
};
C a;
std::cout << a.set.key_comp().r == &a.less << std::endl; // true
C b = std::move(a);
std::cout << b.set.key_comp().r == &b.less << std::endl; // false

Related

How to overload C++ operator, but for only certain inputs?

I have a class for which I would like to overload the addition operators. For my use case, it makes sense only to allow addition in the case where the a variable of each object is equal.
What is the best way to handle the case where they are not? Throw an exception, something else?
class A {
private:
int a, b;
public:
A(int a, int b)
:a(a), b(b) {}
A& operator+=(const A& rhs) {
if (this->a == rhs.a) {
this->b += rhs.b;
return *this;
}
else { //this->a != rhs.a
//what should I put here?
}
}
};
.
Edits:
These objects are created at run-time during file io.
These objects represent a data point in a spectrum. It only makes sense to add the intensities of two data points if they are at the same position.
a is limited to the range (-180.0, 360.0)
This smells like a is the property of the type, not a property of the value... What exactly does this class represent?
The minimally viable (IMHO) way to approach this is to make explicit the transition from an "anything goes" type to a "type compatible with a particular value of a". I.e.:
MyClass x(1,2), y(1,5);
x += y; // won't compile
x.makeCompatibleWith(y) += y; // will compile
It's usually a pessimization to have arithmetic operators like += throw. Instead, have something else assume the cost - then the cost is explicit, and you can keep += nothrow. It's also easy to search the project for costly operations (well, makeCompatibleWith is not super expensive, just more expensive than += since it adds the overhead of exception handling).
Assuming that the invalid cases are meant to be caught in testing, the makeCompatibleWith function could assert the condition it requires, but in release builds it would return some dummy object that turns the += into a no-op since it won't modify x - while still keeping += very simple and quick.
As to what exactly should makeCompatibleWith return: it's up to you. It can be a type that carries a reference, for example:
class MyClass
{
int a, b;
struct Internal
{
MyClass &val;
Internal(MyClass &val) : val(val) {}
MyClass &operator+=(const MyClass &o) noexcept {
val.b += o.b;
return val;
}
MyClass operator+(const MyClass &o) const noexcept {
return { val.a, val.b + o.b };
}
};
public:
MyClass() : a{}, b{} {}
MyClass(int a, int b) : a(a), b(b) {}
Internal makeCompatibleWith(const MyClass &o) noexcept {
thread_local static MyClass dummy;
assert(a == o.a);
if (a != o.a)
return { dummy };
return { *this };
}
};
Note that makeCompatibleWith would be undefined behavior when used from multiple threads if dummy wasn't thread-local.

Why don't reference members refer the assigned variable on nested vectors? What's a proper way to declare an alternative way to access a class member?

I would like to declare an alternative way to access a class member (an array position specifically), as in
class Foo {
int a[2];
int &a_first = a[0];
};
such that any access to a_first in a Foo instance is for all purposes equivalent to accessing a[0] of that same instance.
The code above works as I expected with singular instances and single vectors of the class, but when used on a nested vector the reference address differs from the member address:
#include <iostream>
#include <vector>
class A {
public:
int m;
int &mref = m;
};
int main()
{
A a;
std::cout << (&a.m == &a.mref) << '\n'; // output: 1
std::vector<A> av1(1);
std::cout << (&av1[0].m == &av1[0].mref) << '\n'; // output: 1
std::vector<std::vector<A>> av2(1, std::vector<A>(1));
std::cout << (&av2[0][0].m == &av2[0][0].mref) << '\n'; // output: 0
return 0;
}
I thought reference variables acted as aliases of their assigned variable and were resolved at compile time without being assigned any actual memory at runtime, unlike pointers. Why is this not consistent with the behavior displayed above? What would be a correct way to achieve the alias I want?
The code above works as I expected
Actually it doesn't:
class A {
public:
int m{};
int &mref = m;
};
int main()
{
A a;
A a2 = a;
std::cout << (&a2.m == &a2.mref) << '\n'; // output: 0
};
A reference can be bound only on initialization. Copying will copy the value, not re-bind the reference. So any copy of an object of type A will mess up your reference. This is what happens in your nested vector example. You don't need a nested vector to see this. Try and push in a vector<A>, the vector will have to resize and during the resize will copy its elements, messing your reference.
I thought reference variables acted as aliases of their assigned variable ...
True
... and were resolved at compile time without being assigned any actual memory at runtime, unlike pointers.
Not always. You cannot always resolve at compile time the reference, in which case the reference will actually be implemented with a pointer behind the scenes.
Possible solutions:
use std::reference_wrapper, the copy assignment operator rebinds the reference, but you will need to implement custom copy constructor/assignments for your class:
class A {
public:
int m{};
std::reference_wrapper<int> mref = m;
A() = default;
A(const A& other) noexcept
: m{other.m},
mref{m}
{}
A& operator=(const A& other) noexcept
{
m = other.m;
mref = m;
return *this;
}
};
use a method that returns a reference to the variable
class A {
public:
int m{};
int& mref() { return m; }
const int& mref() const { return m; }
};

What is the meaning of assigning to return value of getter taking this pointer by value?

This doubt came to me when I jumped on an existing code and mistakenly used a getter to set a property,
obj.getProp() = otherProp;
instead of calling the setter,
obj.setProp(otherProp);
I did not realize the mistake because there was no error at compilation or runtime; the assignment resulted in a no-op.
So I came up with the following example, which outputs 337:
#include <iostream>
struct A {
int x = 0;
A(int x) : x(x) {}
A(A& a) : x(a.x) {}
void operator=(A const& other) { x = other.x; }
};
struct B {
A a{3};
int x{3};
A getAbyVal() { return a; }
A& getAbyRef() { return a; }
int getXbyVal() { return x; }
};
int main() {
B b;
std::cout << b.a.x; // this and the other two cout print what I expect, but...
b.getAbyVal() = A{7}; // ... I expected this to fail at compilation time in the first place...
//b.getXbyVal() = 3; // ... just like this fails.
std::cout << b.a.x;
b.getAbyRef() = A{7};
std::cout << b.a.x;
}
So my question is two folds:
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so
that the former compiles and the latter doesn't (beside the fact that
the types are A and int)?
Surprisingly, the difference in types is exactly what makes one compile correctly, and other to fail.
A has an assignment operator defined for it, so compiler dutifully invokes it on the return value (only to discard the whole object later). But the code you wrote supports this. From the compiler view, some other interesting things might have happened in your assignment operator, despite the fact that the object will be eradicated (side effects in formal parlance).
With int as a return value, compiler knows there are no side effects of assigning value to int, so assigning any value to object which is to be eradicated immediately does not make any sense.
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
The difference is precisely that one functions returns a class type, and the other function returns a POD type. A temporary int, for example can't be assigned to:
42 = x; // error
so similarly the language disallows assigning to a temporary int returned from a function as well. This is not the default behavior for user-defined class types so assigning to a temporary A compiles:
A{} = x; // ok
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
Adding a & at the end of is called a ref-qualifier, and allows a user-defined class to have the same semantics as a POD type when it comes to assigning to a temporary. Adding a & at the end of operator= constrains it to only be used on an l-value (basically, a named variable, or a reference returned from a function).
A{} = x; // now error
A a;
a = x; // still ok

Uninitialized value when dealing with shared_ptr

I'm sure this is very simple, but I'm rather new to smart pointers, and I couldn't find an answer to this.
Scenario is very simple:
I have a class A, that holds a shared_ptr to some object X:
class A{
shared_ptr<const X> _asX;
}
now after a series of function calls, I'm creating a new object of type B, that also holds this X. something like:
class B {
private:
shared_ptr<const X> _bsX;
public:
B(): _bsX(nullptr) // - maybe this is problematic {}
foo(shared_ptr<const X>& x)
{
_bsX = x;
// The line above gives me undefined behavior,
// and when I run valgrind I get "Conditional jump or move
// depends on uninitialized value(s)",
// telling me this is not the correct way to do things.
}
Note that it is deliberate the foo really sets the value of _bsX and not the constructor.
So as stated above - depending on the compiler, I something get segmentation faults - which usually means some value was not initialized, and later confirmed by valgrind.
So what should I do - I've tried using 'reset' etc. but I got so confused I'm asking for your help.
Could it be the const ? or the pass by reference ? or the '=' operator.
And while we're at it - should I be passing X with its wrapper (the shared_ptr) to foo, or should I pass the raw pointer, and then make it shared ? if so - could you please give an example. I tried that as well, and got errors.
Ok, I found the problem, and it's no related to smart pointers at all, but since I'm new to this - I thought it might be.
I'll leave this answer for future references. This is what I did (simplified):
class A{
private:
shared_ptr<const int> _num;
public:
A()
{
_num = make_shared<const int>(5);
}
const shared_ptr<const int>& getNum() const {return _num; }
void printNum()
{
cout << *_num.get() << endl;
}
};
class B
{
public:
struct C{
C() : _num(nullptr){}
void boo(shared_ptr<const int> & num) { _num = num;}
shared_ptr<const int> _num;
};
B() {}
void foo(shared_ptr<const int>& num)
{
cs.reserve(2);
for (uint32_t i = 0; i < 2 ; ++i) {
cs.push_back(C()); // This was missing.
cs[i].boo(num);
}
}
void printCNum()
{
for (C c : cs) {
cout << *c._num.get() << endl;
}
}
private:
vector<C> cs;
};
int main()
{
A a{};
shared_ptr<const int> xx = a.getNum();
B b{};
b.foo(xx);
a.printNum();
b.printCNum();
}
Silly me, I thought that when you reserve a vector of Objects (not pointers/references) it also calls their constructor. It turn out it's not. Specifically, I increased the capacity of the vector, but not its size.

Marking a member variable to not copy

I have a class that contains POD members. I need to have all members copied, except one (member a in the example case). The way I'm doing it right now is as follows:
class Example
{
private:
int a = 0;
int b = 0;
int c = 0;
int d = 0;
double e = 0;
public:
Example& operator=(const Example &rhs)
{
b = rhs.b;
c = rhs.c;
d = rhs.d;
e = rhs.e;
return *this;
}
Example() {};
Example(const Example& toCopy)
{
*this = toCopy;
}
};
Is there a way to mark the variable to not copy, as doing it this way is verbose and prone to bugs if I later modify this class?
You can wrap the "odd man out", a, in a struct and define that structs behavior separately:
class Example
{
private:
CopyLess a;
int b = 0;
int c = 0;
int d = 0;
double e = 0;
struct CopyLess {
int a = 0;
CopyLess& operator=(const CopyLess&) { return *this; }
CopyLess(const CopyLess&) {}
};
};
Note that I didn't bother writing any special members for Example anymore because the defaults do what you want. Writing code this way to avoid writing special members as much as possible is called the "Rule of Zero", more information and examples: http://www.nirfriedman.com/2015/06/27/cpp-rule-of-zero/.
No, you cannot make the implicit copy assignment skip members. Defining a custom assignment operator as you have done is the way to achieve your desired behaviour.
Doing it this way is verbose and prone to bugs if I later modify this class
You can group the copied members into a single subobject. That way the custom copy assignment is not fragile to changes and verbosity remains constant in relation to number of copied members.
Bonus: You don't need to specify value initialization for the members separately, but one for the entire subobject is enough.
class Example
{
int a = 0;
struct {
int b;
int c;
int d;
double e;
} state = {};
public:
Example& operator=(const Example &rhs)
{
state = rhs.state;
return *this;
}
...
Since the members are private this won't change the existing API of the class.