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
Related
In the code below std::deque compiles, but std::queue does not:
class A
{
public:
explicit A(int a) : m_a(a)
{
++count;
}
~A()
{
--count;
}
A(A const &) = delete;
A(A && other) : A(other.m_a)
{
other.m_moved = true;
}
A & operator = (const A &) = delete;
A & operator = (A && other)
{
m_a = other.m_a;
other.m_moved = true;
return *this;
}
bool operator == (const A & other) const
{
return m_a == other.m_a;
}
bool operator != (const A & other) const
{
return !operator==(other);
}
bool operator < (const A & other) const
{
return m_a < other.m_a;
}
static int count;
private:
bool m_moved = false;
int m_a;
};
int A::count = 0;
int main()
{
std::deque<A> d;
std::queue q(d);
q.push(A(1));
return 0;
}
but if I make class A copyable by changing A(A const &) = delete; with A(A const &) = default; it starts to compile.
What is the logic behind this? As far as I can see, std::deque is an adapter that does not add some extra functionality.
Elements are copied when you construct queue from deque, but you've deleted copy constructor for deque's value type (A). So you need to move d when constructing queue.
...
int main()
{
std::deque<A> d;
std::queue q(std::move(d)); // added std::move
q.push(A(1));
return 0;
}
There are two simple classes:
class X
{
int value;
public:
X() : value(0) {};
X(int v) : value(v) {};
X(const X& x) { // Does not help!
this->value = x.value;
}
X(const X&& x) noexcept { // Does not help!
this->value = x.value;
}
X& operator=(const X& right) { // Does not help!
this->value = right.value;
return *this;
};
X&& operator=(const X&& right) noexcept { // Does not help!
this->value = right.value;
return std::move(*this);
};
bool operator==(X& right) const {
return this->value == right.value;
};
};
class Y
{
int value;
public:
Y() : value(0) {};
operator X() const {
return X(this->value);
}; // Y objects may be converted to X
};
And sample how I use it:
int main()
{
X x1, x2;
Y y1;
x1 = x2; // Compiles (using the X& operator=)
x1 = std::move(x2); // Compiles (using the X&& operator=)
auto newv = (X)y1; // Compiles
x1 == newv; // Accepted!
x1 == (X)y1; // Error!!!
} // END: main()
x1 == (X)y1 line generates the error C2678: binary '==': no operator found which takes a left-hand operand of type 'X' (or there is no acceptable conversion)
and "no operator == matches these operands, operand types are X==X
I try to compile it with C++17.
Whats wrong with it if line like "x1 == newv" is good for the compiler?
bool operator==(X& right) const
should be
bool operator==(const X& right) const
// ^^
otherwise it cannot be called with a temporary X, which (X)y1 is.
https://ideone.com/tN1w8T
Another problem, which is unrelated to your compiler error from my comment:
bool operator=(X& left) const is this the comparison operator == or the assignment =? The function declaration is a mix and the implementation is clearly comparison.
Assignment would be X& operator=(const X& left) {value = left.value; return *this;}.
#include <compare>
struct A
{
int n;
auto operator <=>(const A&) const noexcept = default;
};
struct B
{
int n;
auto operator <=>(const B& rhs) const noexcept
{
return n <=> rhs.n;
}
};
int main()
{
A{} == A{}; // ok
B{} == B{}; // error: invalid operands to binary expression
}
compiled with clang-10 as clang -std=c++20 -stdlib=libc++ main.cpp
Why does A{} == A{} work but not B{} == B{}?
In the original design of the spaceship operator, == is allowed to call <=>, but this is later disallowed due to efficiency concerns (<=> is generally an inefficient way to implement ==). operator<=>() = default is still defined to implicitly define operator==, which correctly calls == instead of <=> on members, for convenience. So what you want is this:
struct A {
int n;
auto operator<=>(const A& rhs) const noexcept = default;
};
// ^^^ basically expands to vvv
struct B {
int n;
bool operator==(const B& rhs) const noexcept
{
return n == rhs.n;
}
auto operator<=>(const B& rhs) const noexcept
{
return n <=> rhs.n;
}
};
Note that you can independently default operator== while still providing a user-defined operator<=>:
struct B {
int n;
// note: return type for defaulted equality comparison operator
// must be 'bool', not 'auto'
bool operator==(const B& rhs) const noexcept = default;
auto operator<=>(const B& rhs) const noexcept
{
return n <=> rhs.n;
}
};
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.
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.