6

STL コンテナーをきれいに印刷しようとしています。私がしようとしているのは、区切り記号で区切られたコンテナの要素を出力することです。しかし、私はいくつかの問題に遭遇しました。

1. g++ と VC++

ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    cout << s_v;

}

g++ (mingw32 の gcc バージョン 4.4.0) でコンパイルでき、問題なく動作します。VC++ (Visual Studio 9) はこのコードをコンパイルできません。

error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const std::string' (or there is no acceptable conversion)
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\ostream(653): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)'
1>        with
1>        [

どうしてこれなの?このコードは違法ですか? それとも、VC++ beign VC++ だけですか?


2. 未使用のテンプレート変数がコンパイルを中断します。

今、このようにostreamにテンプレートを追加すると(使用されず、そこに座っているだけです)

template <typename T>  // <----- Here
ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    cout << s_v;

}

gcc は演算子と一致しなくなりました。

    error: no match for 'operator<<' in 'std::cout << s_v'

and a lot more candidates...

なんで?テンプレートは未使用です。それは重要ですか?


編集:これは解決されました。私は戻らなければなりませんでした。

3. 使用テンプレート

template <typename T>
ostream& operator<<(ostream& o, const vector<T>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<T>(o,","));

    return o; // Edited
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    vector<int> i_v;
    i_v.push_back(1);
    i_v.push_back(2);

    cout << s_v;
    cout << i_v;
}

私が知っている場合は、テンプレートタイプを使用してください。g++ はそれをコンパイルできますが、例外で終了します。

terminate called after throwing an instance of 'std::bad_cast'
  what():  std::bad_cast

VC++ はただ座って、gcc がこれらすべてを行うのを見ているだけです。それらのいずれもコンパイルしません。

誰かが私のためにこのことを明確にしてもらえますか? ありがとうございました。

4

2 に答える 2

5

前提:

まず第一に、このコードはreturnステートメントを欠いているため違法です (これが、3 番目のバージョンで発生する例外の原因である可能性があります)。

ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
    return o; // <== THIS ONE WAS MISSING
}

これにより、未定義の動作がプログラムに挿入されます。C++11 標準のパラグラフ 6.6.3/1 によると、実際には次のようになります。

[...] 関数の最後を流れることは、値のない戻りと同等です。これにより、値を返す関数で未定義の動作が発生します

最初の質問について:

それが修正されれば、コードは問題なく、VC9 に同梱されている標準ライブラリの実装にはおそらくバグがあります。

実際、コンパイラはoperator <<、引数の名前空間 ( std) および呼び出しが行われている名前空間 (グローバル名前空間) で、適格なオーバーロードを検索する必要があります。演算子がグローバル名前空間で定義され、ステートメントがグローバル名前空間にある限りオーバーcout << s_vロードの解決は正常にオーバーロードを選択する必要があります。

2番目の質問について:

なんで?テンプレートは未使用です。それは重要ですか?

それは簡単です。コンパイラにはT関数の引数から推測する方法がないため、明示的に指定しない限り、コンパイル エラーが発生します。ただし、テンプレート引数を明示的に指定すると、次のようなことを行うことになり、意味がありません。

::operator << <void>(std::cout, s_v);

C++11 では、 のデフォルトの引数を指定できますT。これにより、関数呼び出しが正当化されます。また、バグになりますが、何の目的でしょうか?

3番目の質問について:

T推定されたコンテキストで少なくとも 1 つの関数パラメーターの型で が使用されている場合、コンパイラは関数の引数からそれを推定することを許可します (この場合は を推定しT = std::string、明示的に指定する必要はありません。

結論:

要約すると、必要なreturnステートメントを追加した後、プログラムの最初と 3 番目のバージョンは有効で意味がありますが、2 番目のバージョンは正しくなく、意味もありません。

于 2013-04-02T17:49:18.663 に答える
1
  1. 投稿されているように、コードはどのコンパイラでもコンパイルしないでください。あなたは含まれていません、そして多くのstd::. 私が考えているのは、必要なインクルード がすべて揃っていないということです。特に、それ#include <string>が欠落しており、g++ が間接的にそれを取得します。好奇心旺盛です。通常、問題はその逆です。つまり、VC++ が多くの余分なインクルードを検出します。ただし、場合によっては、部分的にしかわかりません (そのため、 については知ってstd::stringいても、 に関連付けられている非メンバー関数については知らない場合がありますoperator<<)。ただし、実際のインクルードを見ないと、なんとも言えません。

  2. コンパイラは、関数テンプレートではなく、関数に対してのみオーバーロードの解決を実行できます。オーバーロードの解決を開始する前に、関数テンプレートを正しい名前でインスタンス化しようとします。インスタンス化が成功すると関数が生成され、それがオーバーロード セットに追加されます。しかし、関数テンプレートをどのようにインスタンス化することになっていますか。何に使用するかを知る方法はありませんT。そのため、インスタンス化されず (テンプレート引数の推論が失敗します)、そのインスタンスがオーバーロード セットに入る方法が見つかりません。

  3. ここではすぐには何も見えません。不足しているリターンを に追加するoperator<<と、VC++ で正しくコンパイルおよび実行されます。

于 2013-04-02T17:52:56.310 に答える