ベクトルの内容を印刷するにはどうすればいいですか? 質問する

ベクトルの内容を印刷するにはどうすればいいですか? 質問する

の内容をstd::vector画面に印刷するにはどうすればいいですか?


以下を実装するソリューション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;
    }
}

ベストアンサー1

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

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

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

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

charfor ループ ステートメントの型は、ベクトルの要素の型である必要があり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"
}

ifor ループ内でものコピーされた値を変更できないようにしたい場合は、 の型を次のよう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 / 型エイリアス (C++11) / auto (C++11)

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

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

ここで、 の型はi非定数になります (つまり、コンパイラは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 << ' ';

サイドノート:

この時点で、イテレータについてこれまでに聞いたことがあるかもしれませんし、イテレータが「使用すべき」ものであると聞いたことがあるかもしれませんし、その理由を疑問に思っているかもしれません。その答えを理解するのは簡単ではありませんが、簡単に言えば、イテレータは操作の詳細からユーザーを保護する抽象化であるという考え方です。

詳細を自分で記述するよりも (「詳細」とは、ベクトルの要素に実際にアクセスするコードです)、必要な操作 (順次アクセスなど) を実行するオブジェクト (反復子) があると便利です。for ループでは、反復子に値を返すように要求しているだけであることに留意してください ( *i、ここで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_type。これは、メソッドによって返されるタイプですsize

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)

見るジョシュアの答えSTL アルゴリズムを使用してstd::copy、ベクトルの内容を出力ストリームにコピーできます。私はこの方法を使用しないということ以外、特に付け加えることはありません。ただし、習慣以外にそれを行う理由はありません。

std::ranges::copy (C++20)

完全性のために、C++20 では範囲が導入されました。これは の範囲全体に作用できるためstd::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。上記の他のソリューションのいずれかを使用するのも同様に簡単です。

結論

ここで紹介したソリューションはどれも機能します。どれが「最良」かは、あなた次第です (コンテキストやコーディング標準にもよります)。これより詳細なことは、長所と短所を適切に評価できる別の質問に残しておくのが最善でしょうが、いつものようにユーザーの好みが常に影響します。紹介したソリューションはどれも客観的に間違っているわけではありませんが、各コーダーにとってより見栄えのよいものもあるでしょう。

補遺

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

おすすめ記事