3

I am making a class to store a reference to an object if given an lvalue in the constructor and to store a copy if given an rvalue. This is fine and compiles:

template <typename obj_t>
struct holder
{
    obj_t obj;

    template <typename obj_ref_t>
    holder(obj_ref_t && o)
        : obj(std::forward<obj_ref_t>(o))
    {}
};

template <typename obj_t>
holder<obj_t> make_held(obj_t && o) {
    return holder<obj_t>(std::forward<obj_t>(o));
}

However, when I add a destructor (even an empty one) to the class, I get a compiler error:

Invalid instantiation of non-const reference of type "obj_t &" from an rvalue of type "holder<obj_t &>"

Why does adding a destructor somehow invoke a constructor call to create a wrapped object out of an already wrapped object?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
CJ13
  • 111
  • 1
  • 4

1 Answers1

4

A constructor template can never be a copy/move constructor

template <typename obj_ref_t>
holder(obj_ref_t && o)                  // not a move constuctor

When the destructor definition is absent, you're making use of the implicitly defined move constructor for holder. The presence of a user defined destructor will suppress this implicit move constructor definition.

Now, the only remaining viable candidate is your converting constructor template above, but that doesn't work if obj_ref_t is of type holder<T>, this constructor is required to initialize the return value of make_held. So a call like this fails

auto h = make_held(42);

In short, don't define a destructor, or if you have to, define a move constructor.


When defining constructor templates like the one you have, be aware that they might be preferred over move constructors in some cases. There are quite a few answers on SO about this, here's one that describes the problem and has a solution for constraining the constructor template.


If you were to compile your code with a C++17 compiler, the above would compile even with a destructor definition because of guaranteed copy elision.

Praetorian
  • 106,671
  • 19
  • 240
  • 328