Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preventing L-value instantiation in C++14

Tags:

c++

c++14

Methods in C++14 can tell whether they're called on an L-value or an R-value:

struct A{
  A() { puts("Ctor"); }
  void m() const & { puts("L-value"); }
  void m() const && { puts("R-value"); }
};

int main(){
  A a; //Ctor
  a.m() //L-value
  A().m(); //Ctor; R-value
}

Can a ctor tell which type it's constructing? Can I completely disable the construction of L-values from my class?

I have a proxy class (several, actually), which should always convert to something else. Using it without converting is an error. I can detect that error at runtime, e.g., by adding a bool used_ = 0; member #ifndef NDEBUG; and setting it in my user-specified cast, and then doing assert(used_) in the proxy class's Dtor, however it would be much nicer if I could get to compiler to prevent instatiation of L-value instances of that proxy in the first place:

 auto x = Proxy().method1().method2(); // no
 Proxy p; // no
 Target x = Proxy(); //yes
 Target x = Proxy().method1().method2(); //yes

Can I do something like that with C++14?

like image 279
PSkocik Avatar asked Mar 07 '16 15:03

PSkocik


2 Answers

struct temporary_only {
  static temporary_only make() { return {}; }
  temporary_only(temporary_only&&)=delete;
  int get()&& { return 3; }
private:
  temporary_only() {}
};

int main() {
  //temporary_only x; // illegal
  std::cout << temporary_only::make().get() << '\n'; // legal
}

live example.

We disable all public ctors (including copy/move), so nobody can create a temporary_only except via temporary_only::make (an rvalue).

Note that

temporary_only&& a = temporary_only::make();

still works. Here we have an rvalue bound to an rvalue reference, and that rvalue reference is itself an lvalue with a multi-line lifetime (scope lifetime). This cannot be stopped.

like image 179
Yakk - Adam Nevraumont Avatar answered Nov 12 '22 21:11

Yakk - Adam Nevraumont


Why, of course:

#include <iostream>
using namespace std;

class B;

class A
{
public:
    A(B&& b) {}

    A(const B&) = delete;
};

class B {};

int main()
{
    B b;

    // A a1; <- error
    // A a2 = b; // <- error
    A a3 = move(b); // <- fine

    return 0;
}
like image 39
IceFire Avatar answered Nov 12 '22 21:11

IceFire