C++20 std::rangesにおける完全転送のためのstd::forwardと同等のムーブまたはコピー
このライブラリを使用する際、完全転送という概念を理解することが重要です。これは、関数呼び出し時に引数を可能な限り効率的に転送することを意味します。
C++14では、std::forward
を使用して完全転送を実現できます。これは、引数をそのまま受け取り、その型に関わらず適切な参照型に変換します。
しかし、std::ranges
では、std::forward
を使用するだけでは完全転送が保証されない場合があります。これは、std::ranges
アルゴリズムが引数を反復処理するためです。
そこで、C++20では、std::ranges
における完全転送のために、以下の2つの新しい型が導入されました。
std::move_or_copy
std::forward_range
std::move_or_copy
は、引数をムーブまたはコピーする型です。これは、引数の型が可変長配列または参照型の場合に特に役立ちます。
以下の例では、std::move_or_copy
を使用して、可変長配列を引数として渡しています。
#include <ranges>
void print_array(std::move_or_copy<int[]> array) {
for (int i : array) {
std::cout << i << " ";
}
std::cout << std::endl;
}
int main() {
int array[] = {1, 2, 3, 4, 5};
print_array(array);
}
このコードは、以下の出力を生成します。
1 2 3 4 5
std::forward_range
は、範囲を完全に転送するための型です。これは、範囲が反復処理される際に、要素がムーブまたはコピーされるのを防ぎます。
以下の例では、std::forward_range
を使用して、範囲をアルゴリズムに渡しています。
#include <ranges>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 範囲を反復処理し、各要素に2を足す
std::ranges::for_each(std::forward_range(numbers), [](int& n) {
n *= 2;
});
// 結果を出力
for (int n : numbers) {
std::cout << n << " ";
}
std::cout << std::endl;
}
2 4 6 8 10
C++20のstd::ranges
ライブラリを使用する際は、std::move_or_copy
とstd::forward_range
を使用して完全転送を行うことを推奨します。
#include <ranges>
#include <algorithm>
#include <vector>
// 可変長配列を引数として渡す
void print_array(std::move_or_copy<int[]> array) {
for (int i : array) {
std::cout << i << " ";
}
std::cout << std::endl;
}
// 範囲を反復処理し、各要素に2を足す
void multiply_by_two(std::forward_range<int> range) {
for (int& n : range) {
n *= 2;
}
}
int main() {
// 可変長配列
int array[] = {1, 2, 3, 4, 5};
print_array(array);
// 範囲
std::vector<int> numbers = {1, 2, 3, 4, 5};
multiply_by_two(numbers);
// 結果を出力
for (int n : numbers) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
1 2 3 4 5
2 4 6 8 10
解説
可変長配列を引数として渡す
print_array
関数は、可変長配列を引数として受け取ります。この関数は、std::move_or_copy
を使用して、引数を完全に転送します。
範囲を反復処理し、各要素に2を足す
multiply_by_two
関数は、範囲を引数として受け取ります。この関数は、std::forward_range
を使用して、範囲を完全に転送します。
この関数は、範囲内の各要素を反復処理し、2を掛けます。
C++20 std::rangesにおける完全転送のための他の方法
std::ranges::views::all
std::ranges::views::all
は、範囲内のすべての要素を包含するビューを生成します。このビューを使用して、完全転送を行うことができます。
以下の例では、std::ranges::views::all
を使用して、可変長配列を範囲に変換し、アルゴリズムに渡しています。
#include <ranges>
#include <algorithm>
int main() {
int array[] = {1, 2, 3, 4, 5};
// 可変長配列を範囲に変換
auto range = std::ranges::views::all(array);
// 範囲を反復処理し、各要素に2を足す
std::ranges::for_each(range, [](int& n) {
n *= 2;
});
// 結果を出力
for (int n : array) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
2 4 6 8 10
#include <ranges>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// ベクトルを範囲に変換
auto range = std::ranges::views::ref(numbers);
// 範囲を反復処理し、各要素に2を足す
std::ranges::for_each(range, [](int& n) {
n *= 2;
});
// 結果を出力
for (int n : numbers) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
2 4 6 8 10
テンプレート特化
テンプレート特化を使用して、特定の型に対して完全転送を行うようにコードを記述することができます。
以下の例では、テンプレート特化を使用して、可変長配列に対して完全転送を行うようにprint_array
関数を特化しています。
#include <ranges>
#include <algorithm>
template <typename T>
void print_array(T array) {
for (int i : array) {
std::cout << i << " ";
}
std::cout << std::endl;
}
// 可変長配列に対する特化
template <>
void print_array(int* array) {
// 完全転送を行うコード
std::ranges::for_each(std::forward_range(array), [](int& n) {
std::cout << n << " ";
});
std::cout << std::endl;
}
int main() {
int array[] = {1, 2, 3, 4, 5};
print_array(array);
return 0;
}
1 2 3 4 5
C++20 std::rangesライブラリで完全転送を行うには、いくつかの方法があります。
どの方法を使用するかは、状況によって異なります。
- 汎用性が必要な場合は、
std::move_or_copy
またはstd::forward_range
を使用します。 - 効率性を重視する場合は、テンプレート特化を使用します。
c++ c++20 perfect-forwarding