Search code examples
c++c++11copymove-semanticsapi-design

Collapse overloads for copy and move semantics


Many methods have overloads for copy and move semantics. For example, construct and assignment from another type,

T(const U&);
T(U&&);

T& operator=(const U&);
T& operator=(U&&);

More often than not, implementations of the two overloads are quite similar, which kind of leads to code redundancy. So I'm considering collapsing the two into one.

One method I find actually in use is to pass by value and let the value construction do the trick. For example,

constexpr atomic_shared_ptr(shared_ptr<T> desired) noexcept;

where desired may be either copy-constructed or move-constructed. However, pass-by-value is no panacea. Again, take atomic_shared_ptr as an example,

bool compare_exchange_weak(std::shared_ptr<T>&, const std::shared_ptr<T>& d...
bool compare_exchange_weak(std::shared_ptr<T>&, std::shared_ptr<T>&& d...

Unlike the construction/assignment case, depending on the compare result, d may not always be copied. If d is passed by value, it will always be copied, and the copy is not cheap.

Another solution I come up with is to use forwarding reference.

template <typename U>
bool compare_exchange_weak(std::shared_ptr<T>&, U&& d...
  ... std::forward<U>(d) ...

However, this formulation is too permissible/tolerable.

Any other idea or suggestion?


Solution

  • You could constrain the template:

    template <typename U>
    std::enable_if_t<std::is_same_v<std::decay_t<U>,std::shared_ptr<T>>,bool> 
    compare_exchange_weak(std::shared_ptr<T>&, U&& d...
      ... std::forward<U>(d) ...
    

    With the new concept syntax proposal it could be more readable:

    template<class T,class V>
    concept Same_d = std::is_same_v<std::decay_t<T>,std::decay_t<U>>;
    
    bool compare_exchange_weak(std::shared_ptr<T>&, Same_d{U}<std::shared_ptr<T>>&& d...
      ... std::forward<U>(d) ...