在转发参考上重载

提供转发引用过载时必须非常小心,因为它可能匹配得太好了:

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

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

这里的意图是 A 是可复制的,并且我们有另一个可能初始化其他成员的构造函数。然而:

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

建筑呼叫有两个可行的匹配:

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

两者都是精确匹配,但是 #3 引用了比 #2 更少的 cv 资格对象,因此它具有更好的标准转换序列并且是最好的可行功能。

这里的解决方案是始终约束这些构造函数(例如使用 SFINAE):

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

这里的类型特征是从考虑中公开且明确地从 A 中排除任何 A 或类,这将使得该构造函数在前面描述的示例中形成错误(并因此从重载集中移除)。结果,调用了复制构造函数 - 这就是我们想要的。