Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trick to ensure that "only" rvalues can exist of a particular class?

Tags:

c++

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?

like image 912
JohnB Avatar asked Jul 23 '15 08:07

JohnB


People also ask

What is an R value reference C++?

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.

How do you use rvalue reference in C++?

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&& .

Can lvalue bind to rvalue reference?

An lvalue reference can bind to an lvalue, but not to an rvalue.

What is R value Referene in C++11?

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.


2 Answers

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;
};
like image 152
Jarod42 Avatar answered Oct 09 '22 23:10

Jarod42


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.

like image 23
Pixelchemist Avatar answered Oct 09 '22 21:10

Pixelchemist