Why is Visual Studio not stepping into my assignment operators? - c++

I'm trying to get a firm grasp on move and copy constructors and assignments.
I have the following simple templated class:
template<typename T>
class Box {
public:
// Copy constructor
explicit Box(const T& val) {
value = val;
}
// Move constructor
explicit Box(const T&& val) {
value = val;
}
// Set and get value
void set(T val) { value = val; }
T get() { return value; }
// Copy assignment operator
T& operator=(const T& val) {
value = val;
}
// Move assignment operator
T& operator=(const T&& val) {
value = val;
}
private:
T value;
};
Which is used by the following code:
int main() {
Box<int> iBox{ 1 };
int j = 5;
Box<int> jBox{ j };
iBox = jBox;
return 0;
}
I would expect, if my code is correct, that when I step into iBox = jBox, I should step into one of the assignment overloads. Why is this not happening?

You have not declared an assignment operator taking a right-hand operand of type Box, so the compiler has no other option than to use its generated default assignment operator.
You also have no return statements in your assignment operators, by the way.
Maybe you meant to write:
Box& operator=(const Box& other) {
value = other.value;
return *this;
}
Box& operator=(Box&& val) {
value = other.value;
return *this;
}
Notice that an rvalue reference to const would make little sense.

Related

C++ use std::fill for class with deleted assign operator

Suppose I have a range of objects
class X {
public:
X()
: X(0) {
}
X(size_t num)
: x_(num) {
}
X(const X& other) = delete;
X& operator=(const X& other) = delete;
X(X&& other) {
x_ = exchange(other.x_, 0);
}
X& operator=(X&& other) {
x_ = exchange(other.x_, 0);
return *this;
}
size_t GetX() const {
return x_;
}
private:
size_t x_;
};
With this in mind I can't use std::fill as operator= is deleted. What is the right way to fill the range for this kind of object?
Source object of std::fill is const. const object cannot be used as source of move operation.
You can add helper class with conversion operator to X, every time when *first = value; (look at possible implementation of std::fill) is called, temporary of X is returned and move operation is performed:
template<size_t N>
struct Helper {
X MakeTemp() const { return X(N); }
operator X () const { return MakeTemp(); }
};
int main(){
std::vector<X> v;
v.resize(10);
std::fill(v.begin(), v.end(),Helper<10>{});
Demo

Return type of assignment operators in wrapper class?

I came up with the following code to implement C#-style properties in C++. It's basically a wrapper class around typename T, with user-defined get/set functions:
#include <functional>
template <typename T>
struct property
{
property(std::function<T(void)> getter, std::function<void(const T&)> setter)
: get(getter), set(setter) { }
property(std::function<T(void)> _getter)
: get(_getter), set([](const T&) { throw std::exception(); }) { }
property<T>& operator=(const T& value) { set(value); return *this; } // w i h o s h r ?
T operator=(const T& value) { set(value); return get(); } // h c t u e e e
operator T() { return get(); }
property<T>& operator=(property<T>& value) { set(value); return *this; }; //...
T operator=(property<T>& value) { set(value); return get(); }; //...
property<T>& operator=(property<T>&) = delete;
// arithmetic / assignment
#define OP(__OP__) \
T operator __OP__(const T& rhs) { return get() __OP__ rhs; } \
T operator __OP__##= (const T& rhs) { set(get() __OP__ rhs); return get(); } //...
//property<T>& operator __OP__##= (const T& rhs) { set(get() __OP__ rhs); return *this; } //...
OP(+); OP(-); OP(*); OP(/); OP(%); OP(^); OP(&); OP(|);
#undef OP
// bit shift
#define OP(__OP__) \
T operator __OP__(int rhs) { return get() __OP__ rhs; } \
property<T>& operator __OP__##= (int rhs) { set(get() __OP__ rhs); return *this; }
OP(<<); OP(>>);
#undef OP
// comparison / logic
#define OP(__OP__) bool operator __OP__(const T& rhs) { return get() __OP__ rhs;}
OP(<); OP(>); OP(==); OP(!=); OP(<=); OP(>=); OP(&&); OP(||);
#undef OP
// inc/dec
#define OP(__OP__) \
property<T>& operator __OP__##__OP__() { set(get() __OP__ 1); return *this; } \
T operator __OP__##__OP__(int) { T value = get(); operator __OP__##__OP__(); return value; }
OP(+); OP(-);
#undef OP
T operator ~() { return ~get(); }
bool operator !() { return !get(); }
private:
std::function<T(void)> get;
std::function<void(const T&)> set;
};
struct test
{
property<int> a = property<int>([&]()
{
return x/10;
},
[&](int value)
{
x = value*10;
});
property<int> b = property<int>([&]()
{
return y+10;
},
[&](int value)
{
y = value-10;
});
private:
int x, y;
};
using namespace std;
void main()
{
test x;
x.a = 5;
x.a = x.a + 15;
x.a *= 5;
x.b = x.a;
x.b += x.a / 10;
cout << "property a=" << dec << x.a << endl; // should be 100
cout << "property b=" << dec << x.b << endl; // should be 110
}
My main question here is, should the assignment operators return a T or a property<T>&? Returning a reference seems nicer, yet I want to avoid accidentally reassigning the entire class (including the get/set functions), although I believe overloading operator=(property<T>&) and deleting the copy constructor sufficiently guards against this.
Anyway, both seem to produce the same results, so is there any difference between the two? Which would you prefer and why?
Edit, related question: in the case where a set function is not defined, is there some way to detect assignment at compile time, instead of throwing a run-time exception?
In most cases, returning by copy will work okay, but there are some perverse situations where it won't. Consider the following:
struct A {
A(int i) : i_{i} {}
int i_;
A operator=(A const& A) { i_ = a.i_; return *this; }
};
And then try this:
A a1{1};
A a2{2};
A a3{3};
(a3 = a2) = a1; // What is the value of a3::i_?
Returning by copy means that the a3 = a2 assignment generates a temporary that is reassigned to equal a1. In other words a3::i_ == 2 after the assignment. If you return by reference, a3::i_ == 1.

Uninitialized std::string within a class?

I am a bit perplexed. I am creating a custom "Variant" class, but am running into a strange issue with std::string member.
When I try to assign it from another Variant instance, I get an exception that the string is a bad pointer. I am not sure I understand why this would happen because the string should be an instance when the class is constructed.
Here is my code:
class Variant : ObjectBase {
public:
Variant() {};
Variant(const Value& node) { parse(node); };
ValueType Type = ValueType::Unknown;
Variant& operator= (bool value)
{ Type = ValueType::Boolean; value_.vBool = value; return *this; };
Variant& operator= (std::string value)
{ Type = ValueType::String; value_string_ = value; return *this; };
Variant& operator= (double value)
{ Type = ValueType::Double; value_.vDouble = value; return *this; };
Variant& operator= (int32_t value)
{ Type = ValueType::Int32; value_.vInt32 = value; return *this; };
Variant& operator= (int64_t value)
{ Type = ValueType::Int64; value_.vInt64 = value; return *this; };
Variant& operator= (uint32_t value)
{ Type = ValueType::Uint32; value_.vUint32 = value; return *this; };
Variant& operator= (uint64_t value)
{ Type = ValueType::Uint64; value_.vUint64 = value; return *this; };
Variant& operator= (const Value& node) { parse(node); };
private:
union Any {
int32_t vInt32;
int64_t vInt64;
double vDouble;
bool vBool;
uint32_t vUint32;
uint64_t vUint64;
Any() : vDouble(0) {};
} value_;
std::string value_string_;
};
The exception occurs when I do this:
Variant v1 = "Hello";
Variant v2 = v1; // <--- Here is where it occurs
Now, if I create a manual = operator overload, I can see that v2 value is a bad pointer, but I am not sure why. Here is my overload:
Variant& operator= (const Variant value) {
value_ = value.value_;
value_string_ = value.value_string_;
Type = value.Type;
return *this;
}
Maybe I am tired or something, but it is not obvious to me what am I missing here.
Assuming this code compiles at all (which is not clear, as complete code has not been given) then the statements
Variant v1 = "Hello";
Variant v2 = v1;
invoke constructors, not assignment operators.
In the first, since Variant does not supply a constructor that accepts a string, this means Value does, and an implicit conversion to Value is being done before invoking the constructor for Variant. The problem is therefore either in the constructor for Value, or in working of the parse()` function. Neither of those have been shown though.
The second statement invokes the compiler-generated copy constructor. Which, if the preceding construction has not worked as required, will also introduce problems.

Is there a way to forward all assignment operators (+=, *=, etc.) to implicitly use an overridden direct assignment operator (=)?

I know "forwarding" is an unrelated concept in C++11 (as in "perfect forwarding") but it's the first word that comes to mind for me for describing the problem.
I'm overriding the operator= in a wrapper class Proxy,
template<typename T>
class Proxy
{
public:
enum class State
{
NEVER_SET = 0,
SET
};
operator const T& () const
{
if ( _state != State::SET )
{
throw std::domain_error{ "using unset data" };
}
return _data;
}
Proxy<T>& operator=(const T& val)
{
_data = val;
_state = State::SET;
return (*this);
}
private:
T _data;
State _state = State::NEVER_SET;
};
but find myself also needing to add:
Proxy<T>& operator+=(const T& val)
{
_data = (*this) + val;
_state = State::SET;
return (*this);
}
Proxy<T>& operator-=(const T& val)
{
_data = (*this) - val;
_state = State::SET;
return (*this);
}
Proxy<T>& operator*=(const T& val)
{
_data = (*this) * val;
_state = State::SET;
return (*this);
}
Proxy<T>& operator/=(const T& val)
{
_data = (*this) / val;
_state = State::SET;
return (*this);
}
// ...and so on.
Is there a trick to "forwarding" all assignment operators (+=, -=, *=, /=, %=, >>=, <<=, |=, &=, ^=) so that I don't have to define them? That is, a way to make
Proxy<double> x = 7;
Proxy<double> y = 43;
x += y;
automatically "unravel" to
Proxy<double> x = 7;
Proxy<double> y = 43;
x = x + y; // cast operator converts x and y to double, then direct assigns sum,
// therefore no += needing definition in Proxy<T>
You can use the CRTP, but if you aim to only have an explicit = in your Proxy class, you'll need to provide some access to types for which the other operators are already available. Put another way, you can't say a1 = a2 + a3 if you've defined how to assign but not how to add. I address this below by expecting a get() function that exposes some state that can be operated on. It's far more typical (and probably practical) to explicitly define e.g. += then have + defined in terms of it....
#include <iostream>
template <typename T>
struct Implied_Ops
{
T operator+(const T& rhs) const
{
return rhs.get() + static_cast<const T*>(this)->get();
}
T& operator+=(const T& rhs)
{
return static_cast<T&>(*this) = operator+(rhs);
}
};
struct X : Implied_Ops<X>
{
X(int n) : n_(n) { }
X& operator=(const X& rhs) { n_ = rhs.n_; return *this; }
int get() const { return n_; }
int n_;
};
int main()
{
X x { 10 };
X x2 = x + x;
X x3 = x + x2;
std::cout << x.n_ << ' ' << x2.n_ << ' ' << x3.n_ << '\n';
}
Another approach that shouldn't be overlooked is macros....
Yes with CRTP.
template<class D>
struct plus_equals {
template<class Rhs>
D& operator+=(Rhs&& rhs){
D*self=static_cast<D*>(this);
self->_data = (*self)+std::forward<Rhs>(rhs);
self->_state= State::SET;
return *self;
}
};
then inherit your class Foo publically from plus_equals<Foo>.
Of course you need to write that boilerplate for each operator, so it does not help much for one type.

C++ Templates and operator overloading

I am learning templates and operator overloading. I have written some code but I am confused... Let me explain...
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType():locked(false)
{
value = 0;
}
T operator=(T val)
{
value=val;
return value;
}
tempType<T> operator=(tempType<T> &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
And I did...
int main(void)
{
tempType<int> i;
tempType<bool> b;
tempType<float> f;
i.value = 10;
i = i + f;
return 0;
}
What code I need to write in order to execute
tempType<T> operator=(tempType<T> &val){}
Also, I why operator T() is required?
Unless you implement move semantics, operator= should always take a const & reference to the source value. It should also return a reference to the modified object.
tempType & operator=(T const & val)
{
value=val;
return * this;
}
operator T is an implicit conversion function which allows any tempType object to be treated as an object of its underlying type T. Be careful when specifying implicit conversions that they won't conflict with each other.
An implicit conversion function usually shouldn't make a copy, so you probably want
operator T & ()
{
return value;
}
operator T const & () const
{
return value;
}
Given these, you shouldn't need another overload of operator = because the first overload will simply be adapted by the conversion function to a call such as i = b;.
If a series of conversions will result in the operator=(T const & val) being called, you should avoid also defining operator=(tempType const & val) because the overloads will compete on the basis of which conversion sequence is "better," which can result in a brittle (finicky) interface that may refuse to do seemingly reasonable things.
I think I know all the answers so I might as well post full response.
To override default operator= you should declare it as tempType<T> operator=(const tempType<T> &val){}. Now you need to call the method explicitly via i.operator=(other_i).
If you correct the declaration you can use it like this:
tempType<int> i;
tempType<int> other_i;
i = other_i; // this is what you just defined
The operator T() is called a conversion operator. It is kind of reverse or counter part of the conversion constructor which in your case would be tempType(const &T value).
It is used to convert a class object into a given type. So in your case you would be able to write:
tempType<int> i;
int some_int;
some_int = i; // tempType<int> gets converted into int via `operator int()`
template <class T>
class tempType
{
public:
T value;
bool locked;
tempType() : value(), locked(false)
{
value = 0;
}
//althought legal, returning something different from tempType&
//from an operator= is bad practice
T operator=(T val)
{
value=val;
return value;
}
tempType& operator=(const tempType &val)
{
value=val.value;
return *this;
}
operator T()
{
return value;
}
};
int main(void)
{
tempType<int> a;
tempType<int> b;
a = b;
return 0;
}
in the code, a = b calls the operator.
As for the second question, the operator T() is not needed "by default". In your example, it is used when you write i+f:
i is converted to an int
f is converted to a float
the operation (+) is performed
T tempType<int>::operator=(T val) is called for the assignement