4

次のコードを検討してください。ベクトルのベクトルを ostream に出力しようとしています。

#include <iterator>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

template<typename T>
std::ostream &operator <<(std::ostream &os, const std::vector<T> &v) {
    using namespace std;
    copy(v.begin(), v.end(), ostream_iterator<T>(os, "\n"));
    return os;
}

int main() {
    using namespace std;
    vector<string> v1;
    cout << v1;
    vector<vector<string> > v2;
    cout << v2;
    return 0;
}

文字列のベクトルを出力するステートメントは機能します。文字列のベクトルのベクトルを出力するものはそうではありません。g++ 4.7.0 を使用しています。-std=c++11 フラグを付けて試してみました。C++11 モードでは、エラーの半分のページにこの行が表示されます。

error: cannot bind 'std::ostream_iterator<std::vector<std::basic_string<char> >, char, std::char_traits<char> >::ostream_type {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'

私はそれが何を意味するのか理解していないと思います。誰か説明してくれませんか?std::basic_ostream<char>右辺値参照が何であるかは多かれ少なかれ知っていますが、なぜにバインドしないのかわかりませんstd::basic_ostream<char>&&。よくわかっていないのではないでしょうか。そして、これを行うためのより良い方法はありますか?

前もって感謝します。

4

2 に答える 2

8

あなたが得ているエラーは少し誤解を招くものです。私があなたのプログラムをコンパイルしようとしたとき、テンプレートの嘔吐物をかなり掘り下げなければなりませんでした。

error: no match for 'operator<<' in '*((std::ostream_iterator<std::vector<std::basic_string<char> >, char, std::char_traits<char> >*)this)->std::ostream_iterator<std::vector<std::basic_string<char> >, char, std::char_traits<char> >::_M_stream << __value'

基本的に、コピー アルゴリズムを呼び出すと、名前空間 std 内の << 演算子を使用する出力反復子が使用されました。そこに到達すると、ルックアップは std 名前空間でテンプレート vector<> のオーバーロードを見つけようとすることを指示します (そこに IT が存在するため)。

そのため、名前空間 std でベクター テンプレートのストリーム オペレーターを宣言する必要があります。コードを で囲み、namespace std {}何が起こるか見てみましょう...

あなたがしていることは、基本的に std::vector<> を変更し、以前にはなかった動作を追加していることに注意してください。これを行うことは非標準で未定義であり、簡単に邪魔になる可能性があります。他のオプションを検討することもできます。


これがケーニッヒのルックアップであるというのは間違っていました。そうではありません。問題は、クラスで発生するのと同様の名前の隠蔽です。ここでは、ベースで何かのオーバーロードを宣言します (オーバーライドではありません)。

標準の名前空間では、いくつかの「<<」演算子が宣言されています。これらは基本的に という名前の関数operator <<です。本質的にあなたが持っているのはこれです:

void fun(int);

namespace Test {

  void fun() { fun(3); }

}

int main() {
    Test::fun();
}

fun(int)グローバル名前空間または名前が付けられた関数を持たない任意の名前空間から使用できることに注意してくださいfunTest名前空間からは使用できません。

これが、グローバルに宣言された演算子 << の使用がグローバル名前空間からは正常に機能するが、std名前空間内からは機能しない理由です。名前std空間には、提供しようとしているオーバーロードと同じ名前のものが既にあるため、そのオーバーロードは 内のすべてのものから隠されていますstd。using 宣言を入れることができれば、状況は異なります。

于 2012-05-08T15:20:55.943 に答える
4

次のユーティリティ ライブラリが必要です。


これを自分で行いたい場合 (自分自身を教えるために)、2 つのオーバーロードを次のように定義する必要があります。

  • の場合std::vector<T>:

    template<typename T>
    std::ostream &operator <<(std::ostream &os, const std::vector<T> &v) {
       using namespace std;
       copy(v.begin(), v.end(), ostream_iterator<T>(os, "\n"));
       return os;
    }
    
  • の場合std::vector<std::vector<T>>:

    template<typename T>
    std::ostream &operator <<(std::ostream &os, const std::vector<std::vector<T>> &v) {
       using namespace std;
    
       //NOTE: for some reason std::copy doesn't work here, so I use manual loop
       //copy(v.begin(), v.end(), ostream_iterator<std::vector<T>>(os, "\n"));
    
       for(size_t i = 0 ; i < v.size(); ++i)
            os << v[i] << "\n";
       return os;
    }
    

これらのオーバーロードがある場合、それらを組み合わせてこれらのケースを再帰的に処理します。

std::vector<int>  v;
std::vector<std::vector<int>>  vv;
std::vector<std::vector<std::vector<int>>>  vvv;
std::vector<std::vector<std::vector<std::vector<int>>>>  vvvv;

std::cout << v << std::endl; //ok
std::cout << vv << std::endl; //ok
std::cout << vvv << std::endl; //ok
std::cout << vvvv << std::endl; //ok
于 2012-05-08T15:11:50.950 に答える