3

我構建一個簡單的容器類,但碰到一些問題(重裝在Visual C++ 2010, rvalue reference bug?的那些)右值,模板的分辨率和拷貝構造函數(在Visual C++ 2010)

#include <cassert> 
#include <utility> 

template<typename T0> 
class MyType { 
public: 
    typedef T0 value_type; 

    // Default constructor 
    MyType() : m_value() { 
    } 

    // Element constructor 
    explicit MyType(const T0 &c_0) : m_value(c_0) { 
    } 

    template<typename S0> 
    explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) { 
    } 

    // Copy constructor 
    MyType(const MyType &other) : m_value(other.m_value) { 
    } 

    MyType(MyType &&other) : m_value(std::forward<value_type>(other.m_value)) { 
    } 

    // Copy constructor (with convertion) 
    template<typename S0> 
    MyType(const MyType<S0> &other) : m_value(other.m_value) { 
    } 

    template<typename S0> 
    MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) { 
    } 

    // Assignment operators 
    MyType &operator=(const MyType &other) { 
    m_value = other.m_value; 
    return *this; 
    } 

    MyType &operator=(MyType &&other) { 
    m_value = std::move(other.m_value); 
    return *this; 
    } 

    template<typename S0> 
    MyType &operator=(const MyType<S0> &other) { 
    m_value = other.m_value; 
    return *this; 
    } 

    template<typename S0> 
    MyType &operator=(MyType<S0> &&other) { 
    m_value = std::move(other.m_value); 
    return *this; 
    } 

    // Value functions 
    value_type &value() { 
    return m_value; 
    } 

    const value_type &value() const { 
    return m_value; 
    } 

private: 
    template<typename S0> 
    friend class MyType; 

    value_type m_value; 
}; 

int main(int argc, char **argv) { 
    MyType<float> t1(5.5f); 
    MyType<double> t2(t1); 

    return 0; 
} 

上面的代碼提供了以下錯誤:

1>ClCompile: 
1> BehaviorIsolation.cpp 
1>behaviorisolation.cpp(18): error C2440: 'initializing' : cannot convert from 'MyType<T0>' to 'double' 
1>   with 
1>   [ 
1>    T0=float 
1>   ] 
1>   No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 
1>   behaviorisolation.cpp(78) : see reference to function template instantiation 'MyType<T0>::MyType<MyType<float>&>(S0)' being compiled 
1>   with 
1>   [ 
1>    T0=double, 
1>    S0=MyType<float> & 
1>   ] 
1>behaviorisolation.cpp(18): error C2439: 'MyType<T0>::m_value' : member could not be initialized 
1>   with 
1>   [ 
1>    T0=double 
1>   ] 
1>   behaviorisolation.cpp(73) : see declaration of 'MyType<T0>::m_value' 
1>   with 
1>   [ 
1>    T0=double 
1>   ] 
1> 
1>Build FAILED. 

如何在不使用鏈接問題中描述的技巧的情況下糾正這個錯誤?

謝謝!

編輯: 最讓我困惑的是爲什麼沒有任何兩個專門的構造函數調用。他們更適合這個電話。

// Copy constructor (with convertion) 
    template<typename S0> 
    MyType(const MyType<S0> &other) : m_value(other.m_value) { 
    } 

    template<typename S0> 
    MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) { 
    } 

回答

2

已鏈接的問題已經回答了這個問題。讓我們來定義

typedef MyType<float> MF; 
typedef MyType<double> MD; 

當你說MD t2(t1);,你會喜歡調用構造MF::MF(const MD &)。但是,構造函數template <typename T> MF::MF(T&&)匹配得更好,因爲它需要T = MD&,因此解析爲MF::MF(MD&),由於缺少const,這更好地匹配。

爲了解決這個問題,你應該基本上擺脫了霍華德建議的構造函數MF(T&&)。既然你只是打算爲值的構造函數,我的第一個建議是將簽名改爲MF(const T &),這已經可以解決你的問題了。另一種解決方案是添加一個帶簽名MF(MD&)(非const)的構造函數。雖然這很醜陋。最後,您可以在呼叫站點呼叫構造函數明確性:MD t2(MF(t1))MD t2(std::forward<MF>(t1))或甚至MD t2(std::move(t1)),如果這是一個選項。

最後請注意,如果你只處理原始成員,還有什麼可以從明確的舉動得到了,所以你還不如不打擾分別定義所有的構造函數。

+0

增加了一個編輯問題 - 爲什麼不是專門的構造函數調用? –

+0

正如我所說。首先,'MD(MF &&)'甚至不會計算,因爲你沒有傳遞右值引用。第二,'MF(const MD&)'失去了針對'T(MF&)'匹配的'MD(T &&)',並且變成了'MD(MF&)'。 (請注意,它是'T'本身就是參考類型,這在'MyType '中不會發生!) –

+0

謝謝!現在我得到了什麼錯誤。此外,它看起來像一個元組類型GCC的創造者和相關的家庭有他們固定與非const同樣的問題(http://gcc.gnu.org/ml/libstdc++/2008-02/msg00047.html)參考構造函數。要按照他們的步驟。 –

2

你的構造:

template<typename S0> 
    explicit MyType(S0 &&c_0) 

是過於籠統和解決問題的最佳辦法是限制可推導出S0類型。這基本上是相關答案的作用。但也許我可以讓它更漂亮。

這是在的std :: C++ 11:

template<typename S0, 
      class = typename std::enable_if 
      < 
       std::is_convertible<S0, T0>::value 
      >::type> 
    explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) { 
    } 

如果這實在是太醜陋,你可能會考慮:

#define restrict_to(x...) class = typename std::enable_if<x>::type 

...

template<typename S0, restrict_to(std::is_convertible<S0, T0>::value)> 
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) { 
} 

請注意,這不僅能解決您的問題,而且如果您的客戶隨後會問如下問題:

std::is_convertible<X, MyType<T>>::type 

他們現在會得到正確的答案。正如你目前編碼,上述特點總是答案是正確的。

+0

增加了一個編輯的問題 - 爲什麼沒有專門的構造函數叫什麼名字? –

+0

請注意,這個漂亮的解決方案使用了VC2010中未實現的新功能。 –

+0

@Bo:我覺得這是可以容易地放棄使用可變參數宏(添加其他對括號)和'enable_if'可以很容易地寫下來。我認爲'is_convertible'可以或多或少準確地模擬... VC2010缺乏什麼? –