2017-07-24 131 views
0

我有一個C++函數,看起來像這樣:如何投射C++模板參數?

template <class T> void MyClass::set(T value) { 
    if (std::is_same<T, std::string>::value) { 
    stringValue_ = value; 
    } else if (std::is_same<T, int>::value) { 
    intValue_ = value; 
    } 
} 

但是,我得到編譯器錯誤。顯然,它認爲,T的類型始終爲std :: string:

assigning to 'int' from incompatible type 'std::__cxx11::basic_string<char>' 

此外,當我試圖將值轉換爲樣(INT)值,我得到鑄造錯誤的int。該函數的全部要點是獲取模板參數值,然後根據參數的實際類型將其分配給類的正確變量。請讓我知道我在這裏犯了哪些誤解或錯誤。

+0

您可以使用['std :: enable_if'](http://en.cppreference)創建兩個不同的'MyClass :: set'函數。com/w/cpp/types/enable_if)根據你的'std :: is_same'條件選擇正確的一個 – CoryKramer

+0

那麼我在這裏試圖做的是無效的?我正在這樣做,以消除代碼。你的解決方案確實會起作用,但會打敗這個目的。 – user1765354

+2

@ user1765354爲什麼'MyClass :: set'需要成爲一個模板,如果唯一的參數將是'int's和'std :: string's?爲什麼不只有兩個非模板重載?它幾乎沒有任何額外的代碼。 – Brian

回答

4

你不能投一個模板參數,以這樣的方式你想要做的事:

template <class T> void MyClass::set(T value) { 
    if (std::is_same<T, std::string>::value) { 
    stringValue_ = value; 
    } else if (std::is_same<T, int>::value) { 
    intValue_ = value; 
    } 
} 

這裏的關鍵概念是一個模板函數得到的全部擴展時它被實例化。

如果說,對於給定的模板實例,T模板參數是一個整數。這個模板然後實例化約的方式如下:

void MyClass::set(int value) 
{ 
    if (false) 
    { 
     stringValue_=value; 
    } 

你這裏嘗試設置stringValue,這想必std::string,到int,不會是非常成功的。僅僅因爲if條件是錯誤的,不會使這段代碼消失。它仍然必須是有效的C++,即使它從未得到執行,並且這是無效的C++。這是編譯錯誤的解釋。

最新的C++ 17標準中的一些特性將使這些類型的構造實際上成爲可能。然而,預C++ 17解決此類問題的一般方法是使用模板特:

template<typename T> void MyClass::set(T value); 

template<> void MyClass::set<int>(int value) 
{ 
    intValue_=value; 
} 

template<> void MyClass::set<std::string>(std::string value) 
{ 
    stringValue_=value; 
} 

或者,完全忘了模板,只是定義了兩個獨立的set()方法。

+0

'MyClass :: set':我認爲模板參數位置錯誤,對於一個實例,它應該是'std :: string'。 –

+0

感謝您指出。 –

2

編譯器錯誤的原因是if語句的兩個分支都將用模板實例化。這顯然是一個錯誤,因爲如果T = int沒有賦值運算符std::string。 C++ 17引入了無視在編譯時的一個分支的方法,叫做if constexpr

template <class T> void MyClass::set(T value) { 
    if constexpr (std::is_same<T, std::string>::value) { 
    stringValue_ = value; 
    } else if (std::is_same<T, int>::value) { 
    intValue_ = value; 
    } 
} 

但實際上,這裏不使用模板(這裏要麼不使用SFINAE !)。只需使用舊的功能重載。

class MyClass 
{ 
    std::string stringValue_; 
    int intValue_; 
public: 
    void set(int); 
    void set(std::string); 
}; 

void MyClass::set(int value) { 
    intValue_ = value; 
} 

void MyClass::set(std::string value) { 
    stringValue_ = value; 
} 

有人可能會說SFINAE實際上是必要的,如果你想覆蓋的可被轉換爲對方類型的整個範圍。

class MyClass 
{ 
    std::string stringValue_; 
    int intValue_; 
public: 
    template < typename T, typename std::enable_if< std::is_arithmetic<T>::value, void** >::type = nullptr > 
    void set(T value) { 
     intValue_ = value; 
    } 

    template < typename T, typename std::enable_if< std::is_same<T,std::string>::value, void** >::type = nullptr > 
    void set(T value) { 
     stringValue_ = value; 
    } 
}; 
+0

這個的全部要點是消除代碼。我的代碼實際上看起來完全像這樣。我試圖在這裏完成不可能的事,還是我以錯誤的方式做事?請回答這個問題。 – user1765354

+0

@ user1765354你做錯了。也許你可以使用'如果constexpr'來自C++ 17,但即使有某種SFINAE,你也必須寫出所有的重載。 –