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

これは、配列からポインター、関数からポインター、左辺値から右辺値、および最上位の定数/揮発性除去(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が)がなければ、そのオーバーロードにはなんらかの時間がかかります。それが実際にあることを教えてくれる特性タイプになります。enable_ifoperator<<is_STS<...>

Testさらに、コンパイラが、あなたがaに変換しようとしていたことを推測する(または少なくとも試行しない)方法はありませんS<string>S<void>この変換は、たとえばの変換コンストラクタによって有効にできますS)。したがって、それを指定する必要があります

  • TestSも(に変換可能)
  • Sを変換するときののテンプレートパラメータはTeststring

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 に答える