2012-07-03 109 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