Is there any trick in C++ that can ensure that a user of a class can generate only rvalues?
Example:
struct PoorClass { /* ... */};
struct EnrichedClass {
explicit EnrichedClass (const PoorClass & poor)
: m_poor (poor)
{
}
/* additional functionality for poor objects */
private:
const PoorClass & m_poor;
}
const EnrichedClass asEnriched (const PoorClass & poor)
{
return EnrichedClass { poor };
}
Now enriched objects should be only temporaries, as they should not survive the wrapped poor objects. Ideally, enriched objects should never be stored in a variable, but only passed as rvalue references to functions.
Is there any way to ensure that, i.e., to get this as fail-safe as possible?
Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.
If the function argument is an rvalue, the compiler deduces the argument to be an rvalue reference. For example, assume you pass an rvalue reference to an object of type X to a template function that takes type T&& as its parameter. Template argument deduction deduces T to be X , so the parameter has type X&& .
An lvalue reference can bind to an lvalue, but not to an rvalue.
In C++11, however, the rvalue reference lets us bind a mutable reference to an rvalue, but not an lvalue. In other words, rvalue references are perfect for detecting whether a value is a temporary object or not.
You may provide only r-value methods for struct EnrichedClass
:
struct EnrichedClass {
explicit EnrichedClass (const PoorClass& poor) : m_poor (poor) {}
/* additional functionality for poor objects */
void foo() &&; // note the && at the end.
private:
const PoorClass & m_poor;
};
Afaik, there's no way to achieve the desired task directly.
I can think of one way of somehow 'working around the problem':
Make a private or protected constructor and copy constructor with a named static construction function.
Something along the lines of:
struct PoorClass { /* ... */ };
struct EnrichedClass
{
EnrichedClass& operator= (const EnrichedClass & enr) = delete;
private:
explicit EnrichedClass(const PoorClass & poor)
: m_poor(poor)
{ }
EnrichedClass(const EnrichedClass & enr)
: m_poor(enr.m_poor)
{ }
public:
static EnrichedClass enrich(const PoorClass & poor)
{
return EnrichedClass{ poor };
}
void stuff() { }
private:
const PoorClass & m_poor;
};
Thus, one cannot copy the instance:
EnrichedClass::enrich(p).stuff(); // works
auto ec = EnrichedClass::enrich(p); // does not
Note: One may still have lvalues of EnrichedClass
via rvalue-references.
void foo(EnrichedClass && e)
{
// e = lvalue here
}
About the rvalue-ref qualification idea:
void bar() && { /* do stuff */ }
If you want to pass your rvalue to a function using rvalue-references you cannot call member function with &&
ref qualification since rvalue-references are still lvalues (i.e. e
is not an rvalue inside foo
above).
You could do:
void foo(EnrichedClass && e)
{
std::move(e).bar();
}
But now you have provided a guarantee for bar
: The object referenced by this
is an rvalue. It is fine to do whatever you like as long as the object is in a valid (yet unspecified) state after the call (i.e. bar
can move from this
).
Thus, after calling bar
you may not know the state of e
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With