在轉發參考上過載

提供轉發引用過載時必須非常小心,因為它可能匹配得太好了:

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 或類,這將使得該建構函式在前面描述的示例中形成錯誤(並因此從過載集中移除)。結果,呼叫了複製建構函式 - 這就是我們想要的。