C++ Overloading on Forwarding Reference


Example

You must be very careful when providing a forwarding reference overload as it may match too well:

struct A {
    A() = default;           // #1
    A(A const& ) = default;  // #2

    template <class T>
    A(T&& );                 // #3
};

The intent here was that A is copyable, and that we have this other constructor that might initialize some other member. However:

A a;     // calls #1
A b(a);  // calls #3!

There are two viable matches for the construction call:

A(A const& ); // #2
A(A& );       // #3, with T = A&

Both are Exact Matches, but #3 takes a reference to a less cv-qualified object than #2 does, so it has the better standard conversion sequence and is the best viable function.

The solution here is to always constrain these constructors (e.g. using SFINAE):

template <class T,
    class = std::enable_if_t<!std::is_convertible<std::decay_t<T>*, A*>::value>
    >
A(T&& );

The type trait here is to exclude any A or class publicly and unambiguously derived from A from consideration, which would make this constructor ill-formed in the example described earlier (and hence removed from the overload set). As a result, the copy constructor is invoked - which is what we wanted.