2012-07-03 108 views
1

此代碼不可編譯。 我無法找到標準中的原因。有人可以解釋嗎?轉換運算符

#include <iostream> 
#include <string> 

template<typename T> 
class S 
{ 
public: 
    explicit S(const std::string& s_):s(s_) 
    { 
    } 
    std::ostream& print(std::ostream& os) const 
    { 
     os << s << std::endl; 
     return os; 
    } 
private: 
    std::string s; 
}; 

template<typename T> 
std::ostream& operator << (std::ostream& os, const S<T>& obj) 
{ 
    return obj.print(os); 
} 

/*template<> 
std::ostream& operator << <std::string> (std::ostream& os, const S<std::string>& obj) 
{ 
    return obj.print(os); 
}*/ 

class Test 
{ 
public: 
    explicit Test(const std::string& s_):s(s_) 
    { 
    } 
    //operator std::string() const { return s; } 
    operator S<std::string>() const { return S<std::string>(s); } 
private: 
    std::string s; 
}; 

int main() 
{ 
    Test t("Hello"); 
    std::cout << t << std::endl; 
} 

編譯器輸出:

source.cpp: In function 'int main()': 
source.cpp:47:17: error: no match for 'operator<<' in 'std::cout << t' 
source.cpp:47:17: note: candidates are: 
In file included from include/c++/4.7.1/iostream:40:0, 
       from source.cpp:1: 
include/c++/4.7.1/ostream:106:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>] 
include/c++/4.7.1/ostream:106:7: note: no known conversion for argument 1 from 'Test' to 'std::basic_ostream<char>::__ostream_type& (*)(std::basic_ostream<char>::__ostream_type&) {aka std::basic_ostream<char>& (*)(std::basic_ostream<char>&)}' 
.... 

回答

4

那是因爲沒有轉換,除了陣列到指針,函數到指針,左值到右值和頂層const的/易失性去除(參見C++ 11或C++ 03,14.8.2.1)在匹配模板函數時被考慮。具體而言,在爲您的operator<<過載而推導出T時,您的用戶定義的轉換運算符Test -> S<string>未被考慮並且失敗。

爲了使這種普遍的超負荷工作,必須在接收端做的所有工作:

template <class T> 
typename enable_if<is_S<T>::value, ostream&>::type operator <<(ostream&, const T&); 

這超負荷會採取任何T,如果不是爲enable_if(這將是不幸的,因爲我們不希望它干擾其他operator<<重載)。 is_S將是一種特質類型,它會告訴你,T實際上是S<...>

另外,有沒有辦法讓編譯器能猜測(或至少它不會嘗試),您打算Test轉換爲S<string>而不是S<void>或任何(這種轉換可以通過例如啓用。在轉換構造S)。所以,你必須指定

  • Test是(轉換至)的S
  • 模板參數的S,轉換Test時,是string

template <class T> 
struct is_S { 
    static const bool value = false; 
}; 

template <class T> 
struct is_S<S<T>> { 
    static const bool value = true; 
    typedef T T_type; 
}; 

template <> 
struct is_S<Test> { 
    static const bool value = true; 
    typedef string T_type; 
}; 

你必須手動將T轉換爲正確的S過載(例如, S<typename is_S<T>::T_type> s = t,或者,如果你想避免不必要的複製,const S<typename is_S<T>::T_type> &s = t)。

+0

但是'is_S :: value'還是假的 - 將實際改善什麼? – aschepler

+0

我不明白'is_S'會如何神奇地將'Test'轉換成'S '來打印它的內容。你能擴展嗎? –

+0

@MatthieuM .:我將其重寫爲版本2.0 – jpalecek

1

@ jpalecek的答案的第一段解釋了問題所在。如果你需要一個解決方法,你可以添加一個聲明,如:

inline std::ostream& operator<< (std::ostream& os, const S<std::string>& s) 
{ return operator<< <> (os, s); } 

自認爲超載是不是一個模板,以S<std::string>隱式轉換將予以考慮。

,但我看不到任何方式爲各類S<T>做到這一點...

+0

謝謝。有用。 – ForEveR