I'm trying to replace a struct value that has a const value in a non const array but it fails to compile with:
Object of type 'Foo' cannot be assigned because its copy assignment operator is implicitly deleted
Here is an example:
struct Foo {
const int id;
int value;
Foo(): id(-1), value(-1) {}
Foo(int id): id(id), value(0) {}
};
int main(int argc, const char * argv[]) {
Foo foos[10];
foos[0] = Foo(1234);
return 0;
}
I'm coming from a swift background where that is valid as it copies the struct value into the array.
I tried making what I thought was a copy assignment operator but with const members what I tried didn't work either.
This is what I was trying to do for the copy assignment operator:
Foo& operator= (Foo newVal) {
// what do I put here!?
return *this;
};
Doing a memcpy to the array works but seems like the wrong sledgehammer for the job.
Being new at c++ I'm unsure what the correct pattern is for this type of flow.
C++ treats const very strictly. Copying such a struct will be painful if possible at all (without UB). Perhaps what you are really looking for is "readonly" instead? In that case you can make fields private with public getters/setters:
struct Foo {
public:
Foo(): id(-1), value(-1) {}
Foo(int id): id(id), value(0) {}
int getId() const {
return id;
}
int getValue() const {
return value;
}
void setValue(int newValue) {
value = newValue;
}
private:
int id;
int value;
};
You can never change a const value after it's been initialized.
From the name id it sounds like move semantics is what you need. Instead of having two valid objects with the same id, you can make it possible to move the data between objects, but not to copy it.
Example:
#include <iostream>
#include <utility>
struct Foo {
Foo() : id(-1), value(-1) {} // default - "invalid" values
Foo(int id, int value) : id(id), value(value) {} // constructor
explicit Foo(int id) : Foo(id, 0) {} // converting ctor, delegating
Foo(const Foo&) = delete; // no copy construction
Foo(Foo&& rhs) : // move construction ok
id(std::exchange(rhs.id, -1)), // take id, give -1 back
value(std::exchange(rhs.value, -1)) // take value, give -1 back
{}
Foo& operator=(const Foo&) = delete; // no copy assignment
Foo& operator=(Foo&& rhs) { // move assignment ok
// give the "moved from" element our id and value and
// take id and value from the "moved from" element
std::swap(id, rhs.id);
std::swap(value, rhs.value);
return *this;
}
int Id() const { return id; } // only const access
int& Value() { return value; } // non-const access in non-const context
int Value() const { return value; } // const access in const context
private:
int id;
int value;
};
int main() {
Foo foos[2];
foos[0] = Foo(1234); // The temporary Foo will have (-1, -1) when it's destroyed
for(auto& f : foos)
std::cout << f.Id() << ' ' << f.Value() << '\n';
}
Output:
1234 0
-1 -1
In C++, an object can be initialized only once. Your constant member is initialized via the default constructor when you declare the array. Then, you attempt to change that value via assignment
foos[0] = Foo(1234);
This fails, and righly so. One thing you could do is to have a container of not Foos, but some sort of pointers to foo. Thus, you will have allocated the memory for the pointers but will create the actual objects only when you can. For example:
Foo* foos[10]; //here you have only the pointers
foos[0] = new Foo(1234); //OK, the object is inialized here
You may consider using smart pointers such as std::unique_ptr instead of raw pointers here.
Times, and C++ standards have changed!
You can now define your own copy-assignment operator for classes that contain const member objects without undefined behavior as of c++20.
This was undefined behavior prior to c++ and remains so for complete const objects but not non-const objects with const members.
https://stackoverflow.com/a/71848927/5282154
Related
The code below is a simplified version of the actual problem I am facing.
Assume I do not have permission to modify class A (as it is external library), and its already widely used in my existing code base.
The const & assignment from a temporary object (direct constructor) which also return a const & member variable via implicit conversion is not valid in this case.
How do I prevent or make it legal in this case so that the caller gets the correct A value?
class A
{
public:
A() { }
A(int _r, int _g, int _b)
: r(_r), g(_g), b(_b)
{
}
~A(){ }
int GetR() const { return r; }
int GetG() const { return g; }
int GetB() const { return b; }
private:
int r = 0;
int g = 0;
int b = 0;
};
class Foo
{
public:
Foo() : Foo(A()) {}
Foo(int _r, int _g, int _b) : a(A(_r, _g, _b)) {}
explicit Foo(const A& _a) : a(_a) {}
Foo& operator=(const A& a)
{
*this = Foo(a);
return *this;
}
operator A() const { return a; }
operator const A&() const { return a; }
private:
A a;
};
int main()
{
const A& a = Foo(200, 100, 300);
std::cout << a.GetR() << a.GetG() << a.GetB() << endl; // I may not get 200 100 300 here as Foo is already out of scope
return 0;
}
Motivation
Some background on why I am implementing a class as above. The actual purpose of class Foo is to contain 2 different objects, which actually has the same purpose, just different way of storing data internally. For example, let's say class A and class B, which stores RGB value of color in int and floating (normalized) respectively. And as mentioned above, I do not have permission to modify class A, and its already widely used in my code base.
There are tons of function in my code base which takes in const A& and const B& as a function param. So I am trying to unify this 2 classes for a particular case, where I can just pass in Foo in those places and it will work as expected.
You can apply ref-qualified member functions (since C++11), i.e. mark the conversion operator with lvalue-reference, to prevent it being called on temporaries (rvalues).
class Foo
{
public:
... ...
operator A() const { return a; }
operator const A&() const & { return a; }
operator const A&() && = delete;
... ...
};
Then
const A& a = Foo(200, 100, 300); // invalid; invokes deleted operator
const A& a = static_cast<A>(Foo(200, 100, 300)); // fine; invokes operator A()
I give the following example to illustrate my question:
class Abc
{
public:
int a;
int b;
int c;
};
class Def
{
public:
const Abc& abc_;
Def(const Abc& abc):abc_(abc) { }
Def& operator = (const Def& obj)
{
// this->abc_(obj.abc_);
// this->abc_ = obj.abc_;
}
};
Here I do not know how to define the copy assignment operator. Do you have any ideas? Thanks.
references cannot be assigned to. You need something that can. A pointer would work, but they're very abusable.
How about std::reference_wrapper?
#include <functional>
class Abc
{
public:
int a;
int b;
int c;
};
class Def
{
public:
std::reference_wrapper<const Abc> abc_;
Def(const Abc& abc):abc_(abc) { }
// rule of zero now supplies copy/moves for us
// use the reference
Abc const& get_abc() const {
return abc_.get();
}
};
A reference cannot be assigned. Due to this, one can only define it via placement new and copy construction:
Def& operator = (const Def& obj)
{
this->~Def(); // destroy
new (this) Def(obj); // copy construct in place
}
But it is really unnecesary. Just use a pointer.
Suppose I have a class Option:
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
if (val_ == nullptr) {
throw std::out_of_range("get on empty Option");
}
return *val_;
}
const T & getOrElse(const T &x) const
{
return val_ == nullptr ? x : *val_;
}
private:
std::shared_ptr<T> val_;
};
The argument passed to Option::getOrElse is the default value to return when this Option is empty:
Option<int> x; // empty
int y = 123;
x.getOrElse(y); // == 123
However, I think the following code is not safe:
Option<int> x;
x.getOrElse(123); // reference to temporary variable!
A safer way would be to return by value from Option::getOrElse, but that would be wasteful when the Option is non-empty. Can I work around this somehow?
UPDATE: I'm thinking about perhaps overloading on the argument type (lvalue/rvalue) of getOrElse, but haven't figured out exactly how to do so.
UPDATE 2: Maybe this?
T getOrElse(T &&x) const { ... }
const T & getOrElse(const T &x) const { ... }
But I think this might be ambiguous because both lvalue and rvalue arguments fit the second version.
However, I think the following code is not safe:
Option<int> x;
x.getOrElse(123); // reference to temporary variable!
You are correct. This is why std::optional::value_or() returns a T and not a T& or T const&. As per the rationale in N3672:
It has been argued that the function should return by constant reference rather than value, which would avoid copy overhead in certain situations:
void observe(const X& x);
optional<X> ox { /* ... */ };
observe( ox.value_or(X{args}) ); // unnecessary copy
However, the benefit of the function value_or is only visible when the optional object is provided as a temporary (without the name); otherwise, a ternary operator is equally useful:
optional<X> ox { /* ... */ };
observe(ox ? *ok : X{args}); // no copy
Also, returning by reference would be likely to render a dangling reference, in case the optional object is disengaged, because the second argument is typically a temporary:
optional<X> ox {nullopt};
auto&& x = ox.value_or(X{args});
cout << x; // x is dangling!
I suggest you follow the same guidelines. If you really need to avoid the copy, use a ternary. This is safe and copyless:
Optional<int> ox = ...;
const int& val = ox ? *ox : 123;
If you really don't, or the Optional is an rvalue anyway, getOrElse() is more concise.
Since users of your class can expect the reference returned from Option::get() to be valid only as along as the the particular instance of the Option object's lifetime, you could reasonably make the same expectation for what is returned from Option::getOrElse().
In that case it might be an acceptable overhead for the object to maintain a collection of things that it needs to keep alive for the client:
#include <list>
#include <memory>
#include <iostream>
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
if (val_ == nullptr) {
throw std::out_of_range("get on empty Option");
}
return *val_;
}
const T & getOrElse(const T &x) const
{
if (val_ == nullptr) {
std::cout << "storing const T &\n";
elses_.push_front(x);
return elses_.front();
}
return *val_;
}
const T & getOrElse(T &&x) const
{
if (val_ == nullptr) {
std::cout << "storing T && by move\n";
elses_.push_front(std::move(x));
return elses_.front();
}
return *val_;
}
private:
std::shared_ptr<T> val_;
mutable std::list<T> elses_;
};
int main()
{
Option<int> x; // empty
int y = 123;
auto rx = x.getOrElse(y); // == 123
auto & rxx = x.getOrElse(42);
std::cout << "rx = " << rx << "\n";
std::cout << "rxx = " << rxx << "\n";
}
The references returned by Option::getOrElse() will be valid for as long as the reference returned from Option::get() would be. Of course, this also means that Option::getOrElse() can throw an exception.
As a small improvement, if the T type can be used as keys for an associative container you could use one of those instead of a std::list and easily avoid storing duplicates.
I'd rather return by reference and let the caller decide, whether he wants to store a reference to or a copy of the returned value.
Can I suggest to re-design this class?
It has a default ctor which can leave the val_ to be nullptr, but it has a get() at the same time which may throw exception because of dereference (*). It also designed to save T in shared_prt but return it as reference.
Let the client to know it's null:
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
return *val_;
}
bool IsNull() const
{
return val_ == nullptr;
}
private:
std::shared_ptr<T> val_;
};
The client code changed from:
Option option;
const T & ref = option.getOrElse(123);
to be:
Option option;
const T & ref = option.IsNull() ? 123 : option.get();
Why I delete the: if (val_ == nullptr) {
Let's make make_shared<> clear:
return a valid pointer, or
throw bad_alloc exception; it does not return null
So IsNull() is also useless, it should be like:
template<typename T>
class Option {
public:
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
return *val_;
}
private:
std::shared_ptr<T> val_;
};
Why to use shared_ptr? option objects can be move or copied several times? or else I prefer to design it like:
template<typename T>
class Option {
public:
Option(T val) noexcept : val_(std::move(val))
{}
const T & get() const
{
return val_;
}
private:
T val_;
};
I have the following code:
struct S
{
S(): a(42) {}
int a;
};
class P
{
public:
P(S const *s): m_s(s ? *s : /*init m_s by default ctor - how to achieve it?*/)
private:
S m_s;
};
I want m_s to be initialized either by copy-constructor or by default constructor, depending on pointer s:
P p1(nullptr); // expect default ctor: p1.m_s.a = 42
S s;
s.a = 84;
P p2(&s); // expect copy ctor: p2.m_s.a = 84
How can I do it in a most elegant way?
You could just write:
class P {
public:
P(S const *s): m_s(s ? *s : S()){}
private:
S m_s;
};
You might find it more elegant to extract the conditional to a separate helper function:
S createS(S const *s) {
return s ? *s : S();
}
This may look like it will perform an unnecessary copy in the case of a nullptr argument but in practice the compiler will perform RVO and optimize out the copy.
Live demo.
A partial solution that works only with null pointer constants would be to add an overload:
P(std::nullptr_t) : m_s() {}
P(const S * s) : m_s(AssertNotNull(s)) {}
Here I used:
template <typename T> T * AssertNotNull(T * p) {
if (!p) std::abort();
return p;
}
If you need a dynamic switch, you could use a helper function:
struct P {
static const S & maybe(const S * s) {
static S x;
return s ? *s : x;
}
P(const S * s) : m_s(maybe(s)) {}
S m_s;
}
This doesn't actually switch between copy and default constructor (since you cannot do that dynamically), but it fakes the effect by copying from a default-constructed instance if the pointer is null.
Is there any possible way to overload operator* in such way that it's assigning and observing functions are defined apart?
class my_class
{
private:
int value;
public:
int& operator*(){return value;}
};
int main()
{
my_class obj;
int val = 56;
*obj = val; // assign
val = *obj; // observe, same operator* is called
}
Sort of -- you can have the operator* return an instance of another class, rather than returning a reference directly. The instance of the other class then defines both a conversion operator and an assignment operator.
(In your sample code, it looks like you've overloaded the multiplication operator when you meant to overload the dereferencing operator; I'll use the dereferencing operator below.)
For example:
class my_class
{
friend class my_class_ref;
public:
my_class_ref operator*() { return my_class_ref(this); }
private:
int value;
};
class my_class_ref
{
public:
operator int() { return owner->value; } // "observe"
my_class_ref& operator=(int new_value) { owner->value = new_value; return *this; } // "assign"
private:
my_class* owner;
my_class_ref(my_class* owner) { this->owner = owner; }
};
There are some caveats. For example, as my_class_ref is implemented with a pointer to its parent class, your code must be careful that my_class_ref always has a lifetime shorter than the lifetime of the corresponding my_class -- otherwise you will dereference an invalid pointer.
In practice, if you pretend that my_class_ref doesn't exist (i.e. never declare a variable with that class) it can work very well.
Write your class like so
class my_class
{
private:
int value;
public:
int operator*() const { // observing
return value;
}
int& operator*() { // assigning
return value;
}
};
Then these operators are dissambiguated by constness, so code like this is possible
int _tmain(int argc, _TCHAR* argv[])
{
my_class a;
*a = 1; // assigning
int k = *(const_cast<my_class const&>(a)); // observing
return 0;
}