コレクションを別の場所にコピー・移動する関数の説明です。
概要
配列やコンテナのコピー・移動を行えます。
範囲はシーケンシャルアクセスが必要です。
範囲の指定方法は、
・std::xxx(C++03): 対象の範囲 [first, last) のイテレータ―を指定します。
・std::ranges::xxx(C++20): 対象の範囲 [first, last) のイテレータ―を指定します。
・std::ranges::xxx(C++20): 対象の範囲を直接指定します。
詳細についてはこちらを参照してください。https://mappuri.com/program/cpp20-algorithm/
コピー
copy
範囲をコピーします。
copy_backward
範囲を後ろからコピーします。
前方の一部が重なる範囲にコピーする場合、copy 関数だと重なる範囲が正しくコピーできません。この場合、copy_backword 関数で後ろの要素からコピーすると回避できます。
逆に後方の一部が重なる範囲にコピーする場合は copy 関数を使用します。
copy_if
範囲の条件を満たす要素をコピーします。
比較関数には、より小さい(<)ときに true を返す関数を指定します。
std::ranges::copy_if(C++20): 射影(projection)関数を指定できます。
copy_n
範囲の先頭の指定数の要素をコピーします。
コピーのサンプル (C++03, C++11 iterator)
#include <iostream>
/** r 内の [first, last) の範囲と値を出力 範囲:[0, 9) 値:1 2 3 4 5 4 5 6 6 */
template<class T, class U, class V>
static void output(T&& r, U&& first, V&& last) {
std::cout << "範囲:[" << std::distance(std::begin(r), first) << ", " << std::distance(std::begin(r), last) << ")";
std::cout << " 値:";
for (auto i = first; i != last; ++i) {
std::cout << *i << " ";
}
std::cout << std::endl;
}
#include <algorithm>
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> r = { 1, 2, 3, 4, 5 };
std::vector<int> o(r.size());
// 範囲をコピーします。(C++03)
auto copy = std::copy(r.begin(), r.end(), o.begin());
output(o, o.begin(), copy); // 範囲:[0, 5) 値:1 2 3 4 5
// 範囲を後ろからコピーします。(C++03)
auto copy_backward = std::copy_backward(r.begin(), r.begin() + 3, r.end());
output(r, r.begin(), r.end()); // 範囲:[0, 5) 値:1 2 1 2 3 <- 全体
output(r, copy_backward, r.end()); // 範囲:[2, 5) 値:1 2 3 <- コピー先
// 範囲の条件を満たす要素をコピーします。(C++11)
auto copy_if = std::copy_if(r.begin(), r.end(), o.begin(), [](int x) { return x & 1; });
output(o, o.begin(), copy_if); // 範囲:[0, 3) 値:1 3 5 <- 奇数のみ
// 範囲の先頭の指定数の要素をコピーします。(C++11)
auto copy_n = std::copy_n(r.begin(), 3, o.begin());
output(o, o.begin(), copy_n); // 範囲:[0, 3) 値:1 2 3 <- 先頭3個
return 0;
}
コピーのサンプル (C++20 range, iterator)
#include <algorithm>
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> r = { 1, 2, 3, 4, 5 };
std::vector<int> o(r.size());
// 範囲をコピーします。(C++20)
auto copy = std::ranges::copy(r, o.begin());
output(o, o.begin(), copy.out); // 範囲:[0, 5) 値:1 2 3 4 5
// 範囲を後ろからコピーします。(C++20)
auto copy_backward = std::ranges::copy_backward(r.begin(), r.begin() + 3, r.end());
output(r, r.begin(), r.end()); // 範囲:[0, 5) 値:1 2 1 2 3 <- 全体
output(r, r.begin(), copy_backward.in); // 範囲:[0, 3) 値:1 2 1 <- コピー元
output(r, copy_backward.out, r.end()); // 範囲:[2, 5) 値:1 2 3 <- コピー先
// 範囲の条件を満たす要素をコピーします。(C++20)
auto copy_if = std::ranges::copy_if(r, o.begin(), [](int x) { return x & 1; });
output(o, o.begin(), copy_if.out); // 範囲:[0, 3) 値:1 3 5 <- 奇数のみ
// 範囲の先頭の指定数の要素をコピーします。(C++20)
auto copy_n = std::ranges::copy_n(r.begin(), 3, o.begin());
output(o, o.begin(), copy_n.out); // 範囲:[0, 3) 値:1 2 3 <- 先頭3個
return 0;
}
移動
ムーブセマンティクスによって要素が移動されます。移動元の範囲から要素は削除されませんが、要素の中身は未定義になります。下記の例では int 値は元の値が残り、string 値は空になっています。
ほかの動作はコピーと同じです。
move
範囲を移動します。
move_backward
範囲を後ろから移動します。
前方の一部が重なる範囲に移動する場合、move 関数だと重なる範囲が正しく移動できません。この場合、move_backword 関数で後ろの要素から移動すると回避できます。
逆に後方の一部が重なる範囲に移動する場合は move 関数を使用します。
移動のサンプル (C++11 iterator)
#include <algorithm>
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<std::string> r = { "aaa", "bbb", "ccc", "ddd", "eee" };
std::vector<std::string> o(r.size());
// 範囲を移動します。(C++11)
auto move = std::move(r.begin(), r.end(), o.begin());
output(r, r.begin(), r.end()); // 範囲:[0, 5) 値: <- 移動元
output(o, o.begin(), move); // 範囲:[0, 5) 値:aaa bbb ccc ddd eee <- 移動先
// 範囲を後ろから移動します。(C++11)
r = { "aaa", "bbb", "ccc", "ddd", "eee" };
auto move_backward = std::move_backward(r.begin(), r.begin() + 3, r.end());
output(r, r.begin(), r.end()); // 範囲:[0, 5) 値: aaa bbb ccc <- 全体
output(r, move_backward, r.end()); // 範囲:[2, 5) 値:aaa bbb ccc <- 移動先
return 0;
}
移動のサンプル (C++20 range, iterator)
#include <algorithm>
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<std::string> r = { "aaa", "bbb", "ccc", "ddd", "eee" };
std::vector<std::string> o(r.size());
// 範囲を移動します。(C++20)
auto move = std::ranges::move(r, o.begin());
output(r, r.begin(), r.end()); // 範囲:[0, 5) 値: <- 移動元
output(o, o.begin(), move.out); // 範囲:[0, 5) 値:aaa bbb ccc ddd eee <- 移動先
// 範囲を後ろから移動します。(C++20)
r = { "aaa", "bbb", "ccc", "ddd", "eee" };
auto move_backward = std::ranges::move_backward(r.begin(), r.begin() + 3, r.end());
output(r, r.begin(), r.end()); // 範囲:[0, 5) 値: aaa bbb ccc <- 全体
output(r, move_backward.out, r.end()); // 範囲:[2, 5) 値:aaa bbb ccc <- 移動先
return 0;
}
出力先のイテレーター
出力先のイテレーターとして以下のものが指定できます。
・インクリメント可能なイテレーター
r.begin() やポインタなどです。指定位置以降に要素が上書きされます。容量をあらかじめ確保しておく必要があります。
・back_insert_iterator
範囲の末尾に要素が追加されます。
・insert_iterator
指定位置以降に要素が挿入されます。
・ostream_iterator
コンソールやファイルなどに出力します。
#include <algorithm>
#include <iostream>
#include <list>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> r = { 1, 2, 3, 4, 5 };
// インクリメント可能なイテレーター
std::vector<int> o(r.size());
auto copy = std::ranges::copy(r, o.begin());
output(o, o.begin(), copy.out); // 範囲:[0, 5) 値:1 2 3 4 5
// back_insert_iterator
std::list<int> o_back_insert = { 0, 0, 0, 0, 0 };
std::ranges::copy(r, std::back_inserter(o_back_insert));
output(o_back_insert, o_back_insert.begin(), o_back_insert.end()); // 範囲:[0, 10) 値:0 0 0 0 0 1 2 3 4 5
// insert_iterator
std::list<int> o_insert = { 0, 0, 0, 0, 0 };
std::ranges::copy(r, std::inserter(o_insert, std::next(o_insert.begin(), 3)));
output(o_insert, o_insert.begin(), o_insert.end()); // 範囲:[0, 10) 値:0 0 0 1 2 3 4 5 0 0
// ostream_iterator
std::ranges::copy(r, std::ostream_iterator<int>(std::cout, ","));
// 1,2,3,4,5,
return 0;
}
コピーと移動のサンプル
copy の場合はコピーコンストラクタが、move の場合はムーブコンストラクタが呼び出されるのが分かります。
#include <algorithm>
#include <iostream>
#include <list>
#include <vector>
class S1 {
public:
int i;
std::string str;
public:
S1(int _i, std::string _str) : i(_i), str(_str) {
std::cout << "constructor(" << i << "," << str << ") ";
}
~S1() {
std::cout << "destructor(" << i << "," << str << ") ";
}
S1(S1 const& s1) {
std::cout << "copy-constructor(" << s1.i << "," << s1.str << ") ";
*this = s1;
}
S1& operator=(S1 const& s1) {
std::cout << "copy-operator=(" << s1.i << "," << s1.str << ") ";
this->i = s1.i;
this->str = s1.str;
return *this;
}
S1(S1&& s1) noexcept {
std::cout << "move-constructor(" << s1.i << "," << s1.str << ") ";
*this = std::move(s1);
}
S1& operator=(S1&& s1) noexcept {
std::cout << "move-operator=(" << s1.i << "," << s1.str << ") ";
this->i = s1.i;
this->str = std::move(s1.str);
return *this;
}
};
int main(int argc, char* argv[]) {
S1 r[] = { { 1, "aaa" }, { 2, "bbb" } };
std::cout << std::endl;
// constructor(1,aaa) constructor(2,bbb)
std::list<S1> o_copy;
std::ranges::copy(r, std::back_inserter(o_copy));
std::cout << std::endl;
// copy-constructor(1,aaa) copy-operator=(1,aaa) copy-constructor(2,bbb) copy-operator=(2,bbb)
for (auto& s1 : o_copy) {
std::cout << s1.i << "," << s1.str << " ";
}
std::cout << std::endl;
// 1,aaa 2,bbb <- コピー先
std::list<S1> o_move;
std::ranges::move(r, std::back_inserter(o_move));
std::cout << std::endl;
// move-constructor(1,aaa) move-operator=(1,aaa) move-constructor(2,bbb) move-operator=(2,bbb)
for (auto& s1 : o_move) {
std::cout << s1.i << "," << s1.str << " ";
}
std::cout << std::endl;
// 1,aaa 2,bbb <- 移動先
for (auto& s1 : r) {
std::cout << s1.i << "," << s1.str << " ";
}
std::cout << std::endl;
// 1, 2, <- 移動元
return 0;
// destructor(1,aaa) destructor(2,bbb) destructor(1,aaa) destructor(2,bbb) destructor(2,) destructor(1,)
}
コメント