エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

std::basic_stringで戯れる

std::vectorの連結で演算子が使えない、という話題に対して。
どういう話かというと、こういうことです。

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

int main(int, char* [])
{
    std::vector<int> v1;
    std::vector<int> v2;

    v1.push_back(10);
    v1.push_back(20);
    v1.push_back(30);

    v2.push_back(100);
    v2.push_back(200);
    v2.push_back(300);

//  v1 += v2;                                  // これをやりたいけど、こうは書けない
    v1.insert(v1.end(), v2.begin(), v2.end()); // これが一般的な解のひとつ

    std::copy(v1.begin(), v1.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "\n";

    return 0;
}


「こんなときはstd::basic_stringを使えばいい」という話を聞きました。
ので、
やってみた。

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

int main(int, char* [])
{
    std::basic_string<int> s1;
    std::basic_string<int> s2;

    s1.push_back(10);
    s1.push_back(20);
    s1.push_back(30);

    s2.push_back(100);
    s2.push_back(200);
    s2.push_back(300);

    s1 += s2;

    std::copy(s1.begin(), s1.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "\n";

    return 0;
}


実行結果。期待どおりのふるまいがえられました。

10 20 30 100 200 300


ぢゃぁ、組み込み型以外だったらどうなるか。ためしてみた。

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

int main(int, char* [])
{
    std::basic_string<std::complex<double> > s1;
    std::basic_string<std::complex<double> > s2;

    s1.push_back(std::complex<double>(10, 10));
    s1.push_back(std::complex<double>(20, 20));
    s1.push_back(std::complex<double>(30, 30));

    s2.push_back(std::complex<double>(100, 100));
    s2.push_back(std::complex<double>(200, 200));
    s2.push_back(std::complex<double>(300, 300));

    s1 += s2;

    std::copy(s1.begin(), s1.end(), std::ostream_iterator<std::complex<double> >(std::cout, " "));
    std::cout << "\n";

    return 0;
}


実行結果。期待どおりのふるまいがえられました。

(10,10) (20,20) (30,30) (100,100) (200,200) (300,300)


これがOKなら自作の型でも大丈夫だろうと、ためしてみた。

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

struct Point3D
{
    int x;
    int y;
    int z;

    Point3D(int x = 0, int y = 0, int z = 0) : x(x), y(y), z(z) {}
};

std::ostream& operator << (std::ostream& out, const Point3D& point)
{
    return out << "(" << point.x << ", " << point.y << ", " << point.z << ")";
}

int main(int, char* [])
{
    std::basic_string<Point3D> s1;
    std::basic_string<Point3D> s2;

    s1.push_back(Point3D(1, 2, 3));
    s1.push_back(Point3D(4, 5, 6));
    s1.push_back(Point3D(7, 8, 9));

    s2.push_back(Point3D(10, 20, 30));
    s2.push_back(Point3D(40, 50, 60));
    s2.push_back(Point3D(70, 80, 90));

    s1 += s2;

    std::copy(s1.begin(), s1.end(), std::ostream_iterator<Point3D>(std::cout, " "));
    std::cout << "\n";

    return 0;
}


実行結果。期待どおりのふるまいがえられました。

(1, 2, 3) (4, 5, 6) (7, 8, 9) (10, 20, 30) (40, 50, 60) (70, 80, 90)


結論。一応std::vectorの代わりをはたせるようです。
要素になる型/クラスにどのようなメンバや演算子やが必要になるかはわかりませんが、仕様の差を承知した上で使うのならば応用がききそうです。


補足。蛇足かもしれませんが、std::basic_stringというのはstd::stringを実現するためのテンプレートです。というか「std::stringstd::basic_string<char>の別名」というのが正しい表現かも。


補足2。「文字列クラスだと途中に0があるとそこで切れたりしないの?」という疑問がわいたりしますが、文字列クラスはゼロ終端文字列ではないので大丈夫だそうです。

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

int main(int, char* [])
{
    std::basic_string<int>   s1;

    s1.push_back(2);
    s1.push_back(1);
    s1.push_back(0);
    s1.push_back(-1);
    s1.push_back(-2);

    std::copy(s1.begin(), s1.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "\n";

    return 0;
}


実行結果。

2 1 0 -1 -2