2

このようなテンプレート関数があります

#include <list>
#include <iostream>

template<typename T>
std::ostream& operator<<(std::ostream& out, const std::list<T>& list){
    out << "[";
    if(!list.empty()){
        typename std::list<T>::const_iterator it = list.cbegin();
        out << *it;
        for (++it; it != list.cend(); ++it){
            out << ", ";
            out << *it;
        }
    }
    out << "]";
    return out;
}

そして、ネストされたクラスを持ついくつかのテンプレートクラス

namespace my{

    template<
        typename T,
        typename U = size_t
    >

    class graph{

    public:
        typedef T dist_t;
        typedef U node_t;

        class node_pt;
        typedef struct arc_t{
            node_pt* from = nullptr;
            node_pt* to = nullptr;
            dist_t weight;
        } arc_t;
        typedef struct arc_pt{
            arc_t arc;
        } arc_pt;
        typedef struct node_pt{
            node_t node;
        } node_pt;

        class arc_iterator{
        public:
            arc_pt* pt = nullptr;
        public:
            arc_pt* operator->() const{
                return pt;
            }

            friend std::ostream& operator<< (std::ostream &out, const arc_iterator& it) {
                out << "(" << it->arc.from->node << "," << it->arc.to->node << "," << it->arc.weight << ")";
                return out;
            }
        };

        class node_iterator{
        public:
            node_pt* pt = nullptr;

        public:

            node_t operator *() const{
                return pt->node;
            }

            friend std::ostream& operator<< (std::ostream &out, const node_iterator& it) {
                out << *it;
                return out;
            }
        };

    };
}

問題を再現するためのコード

namespace my{
    namespace test{
        void run(){     
            typedef my::graph<size_t> graph_t;
            std::list<graph_t::node_t> l1;
            std::list<graph_t::dist_t> l2;
            std::list<graph_t::node_iterator> l3;
            std::list<graph_t::arc_iterator> l4;

            std::cout << l1 << std::endl;
            std::cout << l2 << std::endl;
            std::cout << l3 << std::endl;
            std::cout << l4 << std::endl;
        }
    }
}


int main(){
    my::test::run();
}

問題は、2 つのフレンド メソッドを定義するとコンパイルされないことです。メソッドを 1 つだけ定義し、イテレータ リストの印刷の 1 つにコメントを付けると、機能します。

私が得ているエラーは

src/OTest_Graph.cpp: In member function ‘virtual void my::test::TestGraph::run()’:
src/OTest_Graph.cpp:59:53: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
In file included from /usr/include/c++/4.7/iostream:40:0,
                 from h/OTest_Graph.h:4,
                 from src/OTest_Graph.cpp:1:
/usr/include/c++/4.7/ostream:600:5: error:   initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::list<my::graph<long unsigned int>::node_iterator, std::allocator<my::graph<long unsigned int>::node_iterator> >]’

ここで何が起こっているのか誰か教えてもらえますか?

4

3 に答える 3

4

さて、コードはコンパイルされ、clang ++ で実行されます。このコンピューターでは g++ を試すことができません。

編集:operator<<実際には、g++ でもコンパイルされます。これは、グローバル名前空間にあるメインでのみ使用するため、理にかなっています。あなたの実際のコードは異なると思います\Edit

しかし、私は「ostream lvalue can't bind to ostream&&」エラーに精通しています

説明する方法。operator<<betweenostreamsと anystdクラスを提供する際に問題があります(listあなたの例のように、しかし私はそれを見つけましたvector

ほとんどの場合は機能しますが、オペレーターが名前空間 (my名前空間など) から呼び出されると、機能しなくなります。

なんで?「この operator<< メンバーはどこで検索すればよいか」という理由でしょうか。ostream とリストの間に多くの operator<< が存在する可能性があります - それぞれが異なる名前空間にあります。では、コンパイラはどこでそれを探すのでしょうか?

オペランドでそれぞれの名前空間を調べます(あなたの場合、両方とも fromstdです)。また、呼び出し元の名前空間 (あなたの場合は) にあることもありmyます。

標準によればそうすべきではないため、「時々」と言いますが、g ++はとにかくそれを行います。clang++ はそうではありませんが、代わりにグローバル名前空間を調べます (それが私にとってはうまくいった理由です)

std理想的には、名前空間内に operator<< を配置する必要があります (試してみてください - 動作します)。しかし - それは標準に反しています。そんなことは許されません。それを名前空間に入れることができmy、g++ では動作するはずですが、他のコンパイラでは動作しません。

それは問題だ。ラッパー (自分の名前空間に存在し、stdクラスへの参照のみを保持するクラス) を作成することで「解決」し、印刷できます。

template<class T> struct OutList<T>{
  const std::list<T> &lst;
  OutList(const std::list &l):lst(l){}
};

template<class T> OutList<T> outlist(const std::list<T> &lst){return OutList<T>(lst);}

std::ostream &operator<<(std::stream &out,const OutList<T> &lst){...}

....
std::cout << "list= "<<outlist(list)<<std::endl;

それはきれいではありませんが、それは私が見つけたすべてです...

于 2013-07-28T14:15:08.587 に答える