c++templates

c++ template class store reference or value with same template argument


I want to design a class which can store value or reference, but with the same argument in the template declaration. I can not figure out how to write the template class. Can any one give me some advise

tempalte <typename T>
class Holder {

// ......
Holder(T &value);
Holder(T value);

}

class CannotCopy {

CannotCopy(CannotCopy const&) = delete;

};

Holder<CannotCopy> holder1(CannotCopy())

CannotCopy cannot_copy;

Holder<CannotCopy> holder2(cannot_copy) // passed by reference



Solution

  • So if I understand you correctly, you sometimes want the class to have ownership of the element you pass into it (when constructed in place) and sometimes you want to use it as a reference to ownership stored elsewhere.

    I, unfortunately, speak from experience when I tell you this is a bad idea. At first glance, this looks like a good idea, however, a few abstraction layers later you end up with code that becomes impossible to reason about the ownership.

    A way of doing it is overloading your constructor on the rvalue/lvalue of the argument:

    template <typename T>
    class Holder {
     public:
      Holder(T &&value)
          : m_ownership{std::make_unique<T>(std::move(value))},
            m_value{*m_ownership} {}
      Holder(T &value) : m_value{value} {}
    
     private:
      std::unique_ptr<T> m_ownership;
      T &m_value;
    };
    

    code at compiler explorer

    As you can see, there now is a constructor taking a T && which allows you to pass an rvalue into it and it gets moved. (In the assumption a move constructor is available, which often is the case for non-copyables) This will know it needs ownership and stores the value as a unique_ptr, to then pass a reference of the value inside of the unique_ptr in the m_value.

    If you are calling the constructor with an lvalue, it simply puts it in m_value without using the unique_ptr for the ownership.

    This way, your class has conditional ownership of the value. If you don't like the unique_ptr taking up space, you could (if alignment allows it -> something you could force) do some bit-manipulations to encode if you have ownership or not and write a bunch of custom code.