355

std::vectora の内容を画面に出力するにはどうすればよいですか?


以下を実装するソリューションoperator<<も同様に優れています。

template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
  // ... What can I write here?
}

個別の機能なしで、これまでのところ私が持っているものは次のとおりです。

#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <sstream>
#include <cstdio>
using namespace std;

int main()
{
    ifstream file("maze.txt");
    if (file) {
        vector<char> vec(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
        vector<char> path;
        int x = 17;
        char entrance = vec.at(16);
        char firstsquare = vec.at(x);
        if (entrance == 'S') { 
            path.push_back(entrance); 
        }
        for (x = 17; isalpha(firstsquare); x++) {
            path.push_back(firstsquare);
        }
        for (int i = 0; i < path.size(); i++) {
            cout << path[i] << " ";
        }
        cout << endl;
        return 0;
    }
}
4

29 に答える 29

470

C++11 コンパイラを使用している場合は、範囲ベースの for ループを使用することをお勧めします (以下を参照)。またはイテレータを使用します。ただし、いくつかのオプションがあります。そのすべてについて、以下で説明します。

範囲ベースの for ループ (C++11)

C++11 (およびそれ以降) では、次のような新しい範囲ベースの for ループを使用できます。

std::vector<char> path;
// ...
for (char i: path)
    std::cout << i << ' ';

for ループ ステートメントの型は、整数インデックス型ではなく、charベクトルの要素の型である必要があります。pathつまり、pathは 型std::vector<char>なので、範囲ベースの for ループに現れる型は ですchar。ただし、明示的な型がautoプレースホルダー型に置き換えられていることがよくあります。

for (auto i: path)
    std::cout << i << ' ';

明示的な型を使用するか、autoキーワードを使用するかに関係なく、オブジェクトiには、オブジェクト内の実際のアイテムのコピーである値がありpathます。iしたがって、ループ内のすべての変更は、pathそれ自体では保持されません。

std::vector<char> path{'a', 'b', 'c'};

for (auto i: path) {
    i = '_'; // 'i' is a copy of the element in 'path', so although
             // we can change 'i' here perfectly fine, the elements
             // of 'path' have not changed
    std::cout << i << ' '; // will print: "_ _ _"
}

for (auto i: path) {
    std::cout << i << ' '; // will print: "a b c"
}

このコピーされたiの値を for ループでも変更できないようにしたい場合は、 の型を強制的に次のようにiすることができますconst char

for (const auto i: path) {
    i = '_'; // this will now produce a compiler error
    std::cout << i << ' ';
}

の項目を変更して for ループの外でもpath変更が持続するようにしたい場合は、次のような参照を使用できます。path

for (auto& i: path) {
    i = '_'; // changes to 'i' will now also change the
             // element in 'path' itself to that value
    std::cout << i << ' ';
}

を変更したくない場合でもpath、オブジェクトのコピーにコストがかかる場合は、値によるコピーの代わりに const 参照を使用する必要があります。

for (const auto& i: path)
    std::cout << i << ' ';

イテレータ

C++11 より前の標準的な解決策は反復子を使用することでしたが、それは今でも完全に受け入れられます。それらは次のように使用されます。

std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

for ループでベクトルの内容を変更する場合は、iteratorではなくを使用しますconst_iterator

補足: typedef / type alias (C++11) / auto (C++11)

これは別の解決策ではなく、上記のiterator解決策の補足です。C++11 標準 (またはそれ以降) を使用している場合は、autoキーワードを使用して読みやすくすることができます。

for (auto i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

ここで の型はi非 const になります (つまり、コンパイラはstd::vector<char>::iteratorの型として使用しますi)。これは、beginメソッドを呼び出したため、コンパイラがiそこから型を推測したためです。cbegin代わりにメソッド (const の "c")を呼び出すと、次のiようになりますstd::vector<char>::const_iterator

for (auto i = path.cbegin(); i != path.cend(); ++i) {
    *i = '_'; // will produce a compiler error
    std::cout << *i << ' ';
}

コンパイラによる型の推定に慣れていない場合は、C++11 で型エイリアスを使用して、常にベクトルを入力する必要がないようにすることができます (これは良い習慣です)。

using Path = std::vector<char>; // C++11 onwards only
Path path; // 'Path' is an alias for std::vector<char>
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

C++11 コンパイラにアクセスできない場合 (または何らかの理由で型エイリアス構文が気に入らない場合) は、より伝統的な を使用できますtypedef

typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

サイドノート:

この時点で、以前にイテレータに出くわしたことがあるかもしれませんし、イテレータを使用することが「想定」されていると聞いたことがないかもしれません。その理由を疑問に思っているかもしれません。答えを理解するのは簡単ではありませんが、簡単に言えば、反復子は操作の詳細からあなたを守る抽象化であるということです。

自分で詳細を記述するよりも、必要な操作 (シーケンシャル アクセスなど) を実行するオブジェクト (イテレータ) を使用すると便利です (「詳細」とは、ベクトルの要素に実際にアクセスするコードです)。*ifor ループでは、反復子に値 ( 、は反復子)を返すように要求しているだけであることに注意してください。直接それ自体iと対話することはありません。pathロジックは次のようになります。イテレータを作成し、ループするオブジェクトを与えます ( iterator i = path.begin())。その後、イテレータに次の値を取得するように要求するだけです ( *i)。イテレータがそれをどのように行ったかを正確に心配する必要はありませんでした。それはイテレータの仕事であり、あなたの仕事ではありません。

わかりましたが、ポイントは何ですか? 値を取得するのが簡単ではない場合を想像してみてください。少し手間がかかる場合はどうなりますか?心配する必要はありません。イテレータが処理してくれるからです。イテレータが詳細を整理してくれるので、あとはイテレータに値を尋ねるだけです。さらに、コンテナを別のstd::vectorものに変更するとどうなるでしょうか。理論的には、新しいコンテナー内の要素へのアクセス方法の詳細が変更されても、コードは変更されません。イテレーターがすべての詳細を舞台裏で整理していることを覚えておいてください。したがって、コードを変更する必要はまったくありません。 -- 前と同じように、イテレータにコンテナ内の次の値を要求するだけです。

したがって、これはベクトルをループするためのやり過ぎの混乱のように思えるかもしれませんが、反復子の概念の背後には十分な理由があるため、それらの使用に慣れる必要があります。

索引付け

整数型を使用して、for ループ内のベクトルの要素を明示的にインデックス付けすることもできます。

for (int i=0; i<path.size(); ++i)
    std::cout << path[i] << ' ';

これを行う場合は、コンテナーのメンバー型が使用可能で適切であれば、それを使用することをお勧めします。このジョブに対してstd::vector呼び出されるメンバー型があります。これは、メソッドによって返される型です。size_typesize

typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
for (Path::size_type i=0; i<path.size(); ++i)
    std::cout << path[i] << ' ';

iteratorソリューションよりも優先してこれを使用しないのはなぜですか? 単純なケースではそれを行うことができますが、 を使用すると、iterator上で簡単に概説したいくつかの利点が得られます。そのため、正当な理由がない限り、この方法は避けることをお勧めします。

std::コピー (C++11)

Joshua's answerを参照してください。STL アルゴリズムstd::copyを使用して、ベクトルの内容を出力ストリームにコピーできます。私はこの方法を使用していないことを除いて、追加するものは何もありません。しかし、習慣以外にそれには正当な理由はありません。

std::範囲::コピー (C++20)

完全を期すために、C++20 では範囲が導入されました。これは a の範囲全体に作用するため、 andstd::vectorは必要ありません。beginend

#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

std::vector<char> path;
// ...
std::ranges::copy(path, std::ostream_iterator<char>(std::cout, " "));

最近のコンパイラ (GCC では明らかに少なくともバージョン 10.1 ) を使用していない限り、C++20 の機能が利用可能であっても、範囲がサポートされない可能性があります。

オーバーロード std::ostream::operator<<

以下のクリスの回答も参照してください。オーバーロードで上記のソリューションの1つを実装する必要があるため、これは他の回答を補完するものですが、コードがはるかにクリーンになるという利点があります。これは、std::ranges::copy上記のソリューションを使用する方法です。

#include <iostream>
#include <vector>
#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

using Path = std::vector<char>; // type alias for std::vector<char>

std::ostream& operator<< (std::ostream& out, const Path& v) {
    if ( !v.empty() ) {
        out << '[';
        std::ranges::copy(v, std::ostream_iterator<char>(out, ", "));
        out << "\b\b]"; // use two ANSI backspace characters '\b' to overwrite final ", "
    }
    return out;
}

int main() {
    Path path{'/', 'f', 'o', 'o'};

    // will output: "path: [/, f, o, o]"
    std::cout << "path: " << path << std::endl;

    return 0;
}

Path基本型と同じように、オブジェクトを出力ストリームに渡すことができるようになりました。上記の他のソリューションを使用することも同様に簡単です。

結論

ここに示す解決策はどれも機能します。どちらが「最良」であるかは、あなた (およびコンテキストまたはコーディング標準) 次第です。これよりも詳細なものは、賛否両論を適切に評価できる別の質問に残すのがおそらく最善ですが、常にユーザーの好みが常に関与します: 提示された解決策は客観的に間違っているわけではありませんが、各コーダーにとってより良く見えるものもあります.

補遺

これは、私が投稿した以前のソリューションの拡張ソリューションです。その投稿は注目を集め続けていたので、私はそれを拡張し、ここに投稿された他の優れたソリューション、少なくとも過去に少なくとも一度は個人的に使用したものを参照することにしました. ただし、私が忘れていた、または知らない良い提案がある可能性があるため、読者に以下の回答を確認することをお勧めします.

于 2012-05-25T17:25:53.947 に答える
238

これを行うためのより簡単な方法は、標準のコピー アルゴリズムを使用することです。

#include <iostream>
#include <algorithm> // for copy
#include <iterator> // for ostream_iterator
#include <vector>

int main() {
    /* Set up vector to hold chars a-z */
    std::vector<char> path;
    for (int ch = 'a'; ch <= 'z'; ++ch)
        path.push_back(ch);

    /* Print path vector to console */
    std::copy(path.begin(), path.end(), std::ostream_iterator<char>(std::cout, " "));

    return 0;
}

ostream_iterator は、iterator adapterと呼ばれるものです。ストリームに出力するタイプ (この場合はchar) でテンプレート化されます。cout(別名コンソール出力) は、書き込み先のストリームであり、スペース文字 ( " ") は、ベクターに格納されている各要素の間に出力するものです。

この標準アルゴリズムは強力で、他の多くのアルゴリズムも同様です。標準ライブラリが提供するパワーと柔軟性が、標準ライブラリを非常に優れたものにしています。想像してみてください。わずか1行のコードでベクトルをコンソールに出力できます。区切り文字の特殊なケースに対処する必要はありません。for ループについて心配する必要はありません。標準ライブラリがすべてを行います。

于 2012-07-04T21:30:21.797 に答える
84

このソリューションは、いくつかの変更を加えた Marcelo のソリューションに触発されました。

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

// This works similar to ostream_iterator, but doesn't print a delimiter after the final item
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >
class pretty_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
    typedef TChar char_type;
    typedef TCharTraits traits_type;
    typedef std::basic_ostream<TChar, TCharTraits> ostream_type;

    pretty_ostream_iterator(ostream_type &stream, const char_type *delim = NULL)
        : _stream(&stream), _delim(delim), _insertDelim(false)
    {
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator=(const T &value)
    {
        if( _delim != NULL )
        {
            // Don't insert a delimiter if this is the first time the function is called
            if( _insertDelim )
                (*_stream) << _delim;
            else
                _insertDelim = true;
        }
        (*_stream) << value;
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator*()
    {
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator++()
    {
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator++(int)
    {
        return *this;
    }
private:
    ostream_type *_stream;
    const char_type *_delim;
    bool _insertDelim;
};

#if _MSC_VER >= 1400

// Declare pretty_ostream_iterator as checked
template<typename T, typename TChar, typename TCharTraits>
struct std::_Is_checked_helper<pretty_ostream_iterator<T, TChar, TCharTraits> > : public std::tr1::true_type
{
};

#endif // _MSC_VER >= 1400

namespace std
{
    // Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time.
    // These aren't necessary if you do actually include the headers.
    template<typename T, typename TAllocator> class vector;
    template<typename T, typename TAllocator> class list;
    template<typename T, typename TTraits, typename TAllocator> class set;
    template<typename TKey, typename TValue, typename TTraits, typename TAllocator> class map;
}

// Basic is_container template; specialize to derive from std::true_type for all desired container types
template<typename T> struct is_container : public std::false_type { };

// Mark vector as a container
template<typename T, typename TAllocator> struct is_container<std::vector<T, TAllocator> > : public std::true_type { };

// Mark list as a container
template<typename T, typename TAllocator> struct is_container<std::list<T, TAllocator> > : public std::true_type { };

// Mark set as a container
template<typename T, typename TTraits, typename TAllocator> struct is_container<std::set<T, TTraits, TAllocator> > : public std::true_type { };

// Mark map as a container
template<typename TKey, typename TValue, typename TTraits, typename TAllocator> struct is_container<std::map<TKey, TValue, TTraits, TAllocator> > : public std::true_type { };

// Holds the delimiter values for a specific character type
template<typename TChar>
struct delimiters_values
{
    typedef TChar char_type;
    const TChar *prefix;
    const TChar *delimiter;
    const TChar *postfix;
};

// Defines the delimiter values for a specific container and character type
template<typename T, typename TChar>
struct delimiters
{
    static const delimiters_values<TChar> values; 
};

// Default delimiters
template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "{ ", ", ", " }" };
template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"{ ", L", ", L" }" };

// Delimiters for set
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters<std::set<T, TTraits, TAllocator>, char>::values = { "[ ", ", ", " ]" };
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters<std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"[ ", L", ", L" ]" };

// Delimiters for pair
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
template<typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters<std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };

// Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar> >
struct print_container_helper
{
    typedef TChar char_type;
    typedef TDelimiters delimiters_type;
    typedef std::basic_ostream<TChar, TCharTraits>& ostream_type;

    print_container_helper(const T &container)
        : _container(&container)
    {
    }

    void operator()(ostream_type &stream) const
    {
        if( delimiters_type::values.prefix != NULL )
            stream << delimiters_type::values.prefix;
        std::copy(_container->begin(), _container->end(), pretty_ostream_iterator<typename T::value_type, TChar, TCharTraits>(stream, delimiters_type::values.delimiter));
        if( delimiters_type::values.postfix != NULL )
            stream << delimiters_type::values.postfix;
    }
private:
    const T *_container;
};

// Prints a print_container_helper to the specified stream.
template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const print_container_helper<T, TChar, TDelimiters> &helper)
{
    helper(stream);
    return stream;
}

// Prints a container to the stream using default delimiters
template<typename T, typename TChar, typename TCharTraits>
typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type
    operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container)
{
    stream << print_container_helper<T, TChar, TCharTraits>(container);
    return stream;
}

// Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.
template<typename T1, typename T2, typename TChar, typename TCharTraits>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const std::pair<T1, T2> &value)
{
    if( delimiters<std::pair<T1, T2>, TChar>::values.prefix != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.prefix;

    stream << value.first;

    if( delimiters<std::pair<T1, T2>, TChar>::values.delimiter != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.delimiter;

    stream << value.second;

    if( delimiters<std::pair<T1, T2>, TChar>::values.postfix != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.postfix;
    return stream;    
}

// Used by the sample below to generate some values
struct fibonacci
{
    fibonacci() : f1(0), f2(1) { }
    int operator()()
    {
        int r = f1 + f2;
        f1 = f2;
        f2 = r;
        return f1;
    }
private:
    int f1;
    int f2;
};

int main()
{
    std::vector<int> v;
    std::generate_n(std::back_inserter(v), 10, fibonacci());

    std::cout << v << std::endl;

    // Example of using pretty_ostream_iterator directly
    std::generate_n(pretty_ostream_iterator<int>(std::cout, ";"), 20, fibonacci());
    std::cout << std::endl;
}

Marcelo のバージョンと同様に、サポートされるすべてのコンテナーに特化する必要がある is_container 型特性を使用します。value_typeconst_iteratorbegin()/をチェックするために特性を使用することはend()可能かもしれませんが、それらの基準に一致するが実際にはコンテナーではないものに一致する可能性があるため、それをお勧めするかどうかはわかりませんstd::basic_string. また、Marcelo のバージョンと同様に、使用する区切り文字を指定するために特化できるテンプレートを使用します。

主な違いは、私のバージョンを を中心に構築したことです。pretty_ostream_iteratorこれは と同様に機能しstd::ostream_iteratorますが、最後の項目の後に区切り文字を出力しません。コンテナーのフォーマットは によって行われprint_container_helper、 is_container トレイトなしでコンテナーを出力したり、別の区切り文字タイプを指定したりするために直接使用できます。

is_container と delimiters も定義したので、非標準の述語またはアロケーターを含むコンテナー、および char と wchar_t の両方で機能します。operator<< 関数自体も、char ストリームと wchar_t ストリームの両方で機能するように定義されています。

最後に、std::enable_ifC++0x の一部として利用できる を使用し、Visual C++ 2010 および g++ 4.3 (-std=c++0x フラグが必要) 以降で動作します。この方法では、Boost に依存しません。

于 2011-06-05T04:07:35.320 に答える
79

C++11 では、範囲ベースの for ループを使用できるようになりました。

for (auto const& c : path)
    std::cout << c << ' ';
于 2013-12-10T23:25:09.360 に答える
50

これを行う最善の方法は、operator<<この関数をプログラムに追加してオーバーロードすることだと思います。

#include <vector>
using std::vector;
#include <iostream>
using std::ostream;

template<typename T>
ostream& operator<< (ostream& out, const vector<T>& v) {
    out << "{";
    size_t last = v.size() - 1;
    for(size_t i = 0; i < v.size(); ++i) {
        out << v[i];
        if (i != last) 
            out << ", ";
    }
    out << "}";
    return out;
}

次に<<、その要素もostream& operator<<定義されていると仮定して、可能なベクトルで演算子を使用できます。

vector<string>  s = {"first", "second", "third"};
vector<bool>    b = {true, false, true, false, false};
vector<int>     i = {1, 2, 3, 4};
cout << s << endl;
cout << b << endl;
cout << i << endl;

出力:

{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}
于 2014-04-30T20:46:28.030 に答える
22

for_each+ ラムダ式:

#include <vector>
#include <algorithm>
// ...
std::vector<char> vec;
// ...
std::for_each(
              vec.cbegin(),
              vec.cend(),
              [] (const char c) {std::cout << c << " ";} 
              );
// ...

もちろん、範囲ベースの forは、この具体的なタスクの最も洗練されたソリューションですが、これは他にも多くの可能性を提供します。

説明

このfor_eachアルゴリズムは、入力範囲呼び出し可能なオブジェクトを取り、範囲のすべての要素でこのオブジェクトを呼び出します。入力範囲は 2 つのiteratorによって定義されます。呼び出し可能なオブジェクトは、関数、関数へのポインター、オーバーロードするクラスのオブジェクト、() operatorまたはこの場合のようにラムダ式にすることができます。この式のパラメーターは、ベクターの要素の型と一致します。

この実装の優れた点は、ラムダ式から得られる力です。ベクトルを出力するだけでなく、このアプローチをさまざまなことに使用できます。

于 2014-10-14T14:48:38.330 に答える
21

これは数回編集されており、コレクションをラップするメイン クラスを呼び出すことにしましたRangePrinter

ワンタイム オーバーロードを記述したら、これはどのコレクションでも自動的に機能するはずですoperator<<。ただし、マップがペアを出力するために特別なコレクションが必要であり、そこで区切り文字をカスタマイズする必要がある場合を除きます。

アイテムを直接出力する代わりに、特別な「印刷」関数を使用することもできます。これは、STL アルゴリズムのように、カスタム述語を渡すことができます。用のカスタム プリンタを使用すると、このmapように使用できますstd::pair

「デフォルト」のプリンターは、それをストリームに出力するだけです。

では、カスタム プリンターを作成してみましょう。外部クラスを に変更しRangePrinterます。したがって、2 つの反復子といくつかの区切り記号がありますが、実際の項目を印刷する方法はカスタマイズしていません。

struct DefaultPrinter
{
   template< typename T >
   std::ostream & operator()( std::ostream& os, const T& t ) const
   {
     return os << t;
   }

   // overload for std::pair
   template< typename K, typename V >
   std::ostream & operator()( std::ostream & os, std::pair<K,V> const& p)
   {
      return os << p.first << '=' << p.second;
   }
};

// some prototypes
template< typename FwdIter, typename Printer > class RangePrinter;

template< typename FwdIter, typename Printer > 
  std::ostream & operator<<( std::ostream &, 
        RangePrinter<FwdIter, Printer> const& );

template< typename FwdIter, typename Printer=DefaultPrinter >
class RangePrinter
{
    FwdIter begin;
    FwdIter end;
    std::string delim;
    std::string open;
    std::string close;
    Printer printer;

    friend std::ostream& operator<< <>( std::ostream&, 
         RangePrinter<FwdIter,Printer> const& );

public:
    RangePrinter( FwdIter b, FwdIter e, Printer p,
         std::string const& d, std::string const & o, std::string const& c )
      : begin( b ), end( e ), printer( p ), open( o ), close( c )
    {
    } 

     // with no "printer" variable
    RangePrinter( FwdIter b, FwdIter e,
         std::string const& d, std::string const & o, std::string const& c )
      : begin( b ), end( e ), open( o ), close( c )
    {
    } 

};


template<typename FwdIter, typename Printer>
std::ostream& operator<<( std::ostream& os, 
          RangePrinter<FwdIter, Printer> const& range )
{
    const Printer & printer = range.printer;

    os << range.open;
    FwdIter begin = range.begin, end = range.end;

    // print the first item
    if (begin == end) 
    { 
      return os << range.close; 
    }

    printer( os, *begin );

    // print the rest with delim as a prefix
    for( ++begin; begin != end; ++begin )
    {
       os << range.delim;
       printer( os, *begin );
    }
    return os << range.close;
}

デフォルトでは、キーと値のタイプが両方とも印刷可能であり、そうでない場合 (他のタイプの場合と同様) に独自の特別なアイテム プリンターを配置できる場合、または必要ない場合に限り、マップに対して機能します。区切り文字として「=」。

これらを作成する free-function を最後に移動しています:

フリー関数 (反復子バージョン) は次のようになり、デフォルトを設定することもできます。

template<typename Collection>
RangePrinter<typename Collection::const_iterator> rangePrinter
    ( const Collection& coll, const char * delim=",", 
       const char * open="[", const char * close="]")
{
   return RangePrinter< typename Collection::const_iterator >
     ( coll.begin(), coll.end(), delim, open, close );
}

std::setその後、次のように使用できます

 std::cout << outputFormatter( mySet );

また、カスタム プリンターを使用する自由関数バージョンと、2 つの反復子を使用するバージョンを作成することもできます。いずれにせよ、それらはテンプレート パラメータを解決し、それらを一時的に渡すことができます。

于 2011-01-31T12:12:18.133 に答える
13

これは、私が一緒にハッキングした完全な作業プログラムとして提示された作業ライブラリです。

#include <set>
#include <vector>
#include <iostream>

#include <boost/utility/enable_if.hpp>

// Default delimiters
template <class C> struct Delims { static const char *delim[3]; };
template <class C> const char *Delims<C>::delim[3]={"[", ", ", "]"};
// Special delimiters for sets.                                                                                                             
template <typename T> struct Delims< std::set<T> > { static const char *delim[3]; };
template <typename T> const char *Delims< std::set<T> >::delim[3]={"{", ", ", "}"};

template <class C> struct IsContainer { enum { value = false }; };
template <typename T> struct IsContainer< std::vector<T> > { enum { value = true }; };
template <typename T> struct IsContainer< std::set<T>    > { enum { value = true }; };

template <class C>
typename boost::enable_if<IsContainer<C>, std::ostream&>::type
operator<<(std::ostream & o, const C & x)
{
  o << Delims<C>::delim[0];
  for (typename C::const_iterator i = x.begin(); i != x.end(); ++i)
    {
      if (i != x.begin()) o << Delims<C>::delim[1];
      o << *i;
    }
  o << Delims<C>::delim[2];
  return o;
}

template <typename T> struct IsChar { enum { value = false }; };
template <> struct IsChar<char> { enum { value = true }; };

template <typename T, int N>
typename boost::disable_if<IsChar<T>, std::ostream&>::type
operator<<(std::ostream & o, const T (&x)[N])
{
  o << "[";
  for (int i = 0; i != N; ++i)
    {
      if (i) o << ",";
      o << x[i];
    }
  o << "]";
  return o;
}

int main()
{
  std::vector<int> i;
  i.push_back(23);
  i.push_back(34);

  std::set<std::string> j;
  j.insert("hello");
  j.insert("world");

  double k[] = { 1.1, 2.2, M_PI, -1.0/123.0 };

  std::cout << i << "\n" << j << "\n" << k << "\n";
}

現在はvectorとでのみ動作しますが、特殊set化を拡張するだけで、ほとんどのコンテナーで動作するようにすることができます。IsContainerこのコードが最小限かどうかについてはあまり考えたことがありませんが、余分なものを取り除くことができるものはすぐには思いつきません。

編集:キックのために、配列を処理するバージョンを含めました。さらなるあいまいさを避けるために、char 配列を除外する必要がありました。それでも問題が発生する可能性がありwchar_t[]ます。

于 2011-01-31T11:51:51.110 に答える
8

問題はおそらく前のループにあります。

(x = 17; isalpha(firstsquare); x++)

このループはまったく実行されない (firstsquareアルファベット以外の場合) か、永久に実行されます (アルファベットの場合)。その理由は、増加firstsquareしても変化しないからです。x

于 2012-05-25T07:19:07.283 に答える
8

余分な末尾の区切り記号を使用std::copyせずに使用する

( @ JoshuaKravtiz answerstd::copyで最初に使用されたように)を使用するが、最後の要素の後に追加の末尾セパレーターを含めない代替/修正アプローチ:

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

template <typename T>
void print_contents(const std::vector<T>& v, const char * const separator = " ")
{
    if(!v.empty())
    {
        std::copy(v.begin(),
                  --v.end(),
                  std::ostream_iterator<T>(std::cout, separator));
        std::cout << v.back() << "\n";
    }
}

// example usage
int main() {
    std::vector<int> v{1, 2, 3, 4};
    print_contents(v);      // '1 2 3 4'
    print_contents(v, ":"); // '1:2:3:4'
    v = {};
    print_contents(v);      // ... no std::cout
    v = {1};
    print_contents(v);      // '1'
    return 0;
}

カスタム POD タイプのコンテナーに適用される使用例:

// includes and 'print_contents(...)' as above ...

class Foo
{
    int i;
    friend std::ostream& operator<<(std::ostream& out, const Foo& obj);
public:
    Foo(const int i) : i(i) {}
};

std::ostream& operator<<(std::ostream& out, const Foo& obj)
{
    return out << "foo_" << obj.i; 
}

int main() {
    std::vector<Foo> v{1, 2, 3, 4};
    print_contents(v);      // 'foo_1 foo_2 foo_3 foo_4'
    print_contents(v, ":"); // 'foo_1:foo_2:foo_3:foo_4'
    v = {};
    print_contents(v);      // ... no std::cout
    v = {1};
    print_contents(v);      // 'foo_1'
    return 0;
}
于 2017-07-30T13:34:54.510 に答える
7

C++11 では、範囲ベースの for ループが適切な解決策になる場合があります。

vector<char> items = {'a','b','c'};
for (char n : items)
    cout << n << ' ';

出力:

a b c 
于 2015-03-12T04:00:26.017 に答える
5

オーバーロード演算子<<:

template<typename OutStream, typename T>
OutStream& operator<< (OutStream& out, const vector<T>& v)
{
    for (auto const& tmp : v)
        out << tmp << " ";
    out << endl;
    return out;
}

使用法:

vector <int> test {1,2,3};
wcout << test; // or any output stream
于 2016-08-12T00:14:57.300 に答える
4

以前のアプローチとは異なるアプローチを思いついたので、ここに別の回答を追加します。それは、ロケール ファセットを使用することです。

基本はこちら

基本的にあなたがすることは次のとおりです。

  1. から派生するクラスを作成しますstd::locale::facet。わずかな欠点は、ID を保持するためにどこかにコンパイル ユニットが必要になることです。MyPrettyVectorPrinter と呼びましょう。おそらくもっと良い名前を付けて、ペアとマップ用に作成することもできます。
  2. ストリーム関数で、チェックしますstd::has_facet< MyPrettyVectorPrinter >
  3. それがtrueを返す場合、それを抽出しますstd::use_facet< MyPrettyVectorPrinter >( os.getloc() )
  4. ファセット オブジェクトには区切り文字の値があり、それらを読み取ることができます。ファセットが見つからない場合、印刷関数 ( operator<<) はデフォルトのものを提供します。ベクトルの読み取りでも同じことができることに注意してください。

カスタムオーバーライドを使用しながら、デフォルトの印刷を使用できるため、この方法が気に入っています。

欠点は、複数のプロジェクトで使用する場合にファセット用のライブラリが必要であること (ヘッダーのみにすることはできません) と、新しいロケール オブジェクトを作成する費用について注意する必要があることです。

両方のアプローチが正しい可能性があり、あなたが選択できると信じているため、これを他のソリューションを変更するのではなく、新しいソリューションとして作成しました。

于 2013-02-13T17:55:59.273 に答える
3

2 つの問題があります。で指摘したように

for (x = 17; isalpha(firstsquare); x++)

無限ループがあるか、まったく実行されないかのどちらかです。またif (entrance == 'S')、入り口の文字が異なる場合は'S'、パス ベクトルに何もプッシュされず、空になり、画面に何も出力されません。後者のチェックpath.empty()または印刷をテストできますpath.size()

いずれにせよ、ベクトルの代わりに文字列を使用した方がよいのではないでしょうか? 配列のように文字列の内容にアクセスしたり、文字を検索したり、部分文字列を抽出したり、文字列を (ループなしで) 簡単に出力したりできます。

文字列ですべてを行うことは、より複雑でない方法で記述し、問題を見つけやすくする方法かもしれません。

于 2012-05-25T07:32:50.410 に答える
1

私の解決策は、 sccパッケージの一部であるsimple.hです。すべての std コンテナー、マップ、セット、c-array は印刷可能です。

于 2011-06-19T09:38:44.837 に答える