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

2 回答 2

4

那是因为没有转换,除了数组到指针、函数到指针、左值到右值和顶级 const/volatile 删除(参见 c++11 或 c++03、14.8.2.1),匹配模板函数时考虑。具体来说,在推断您的重载Test -> S<string>时不会考虑您的用户定义的转换运算符,并且会失败。Toperator<<

要使这种通用过载工作,您必须在接收方完成所有工作:

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

T如果不是因为,该重载将采用 any enable_if(这将是不幸的,因为我们不希望它干扰其他operator<<重载)。is_S将是一个特征类型,它会告诉你T事实上是S<...>.

另外,编译器无法猜测(或者至少它不会尝试)您打算转换Test为 aS<string>而不是S<void>或其他任何东西(这种转换可以通过例如转换构造函数来启用S)。所以你必须指定

  • Testis (convertible to) an Stoo
  • 的模板参数S在转换 aTest时为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为正确的(例如,或者,如果您想避免不必要的复制,)。Soperator<<S<typename is_S<T>::T_type> s = tconst S<typename is_S<T>::T_type> &s = t

于 2012-07-03T11:44:14.900 回答
1

@jpalecek 答案的第一段解释了问题所在。如果您需要解决方法,可以添加如下声明:

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

由于该重载不是模板,S<std::string>因此将考虑隐式转换。

但我看不到任何方法可以为所有类型做到这一点S<T>......

于 2012-07-03T12:20:48.660 回答