C++ 標準ライブラリ utility
C++ 標準ライブラリでは、<utility>ヘッダーファイルには、効率的で可読性の高いコードを書く際に非常に役立つ、実用的なユーティリティクラスと関数が含まれています。
utilityこのライブラリの核となる価値は次のとおりです:
- 基本的なデータ構造とツール関数を提供する
- 一般的なプログラミングタスクの実装を簡素化する
- 他の標準ライブラリコンポーネントに基本サポートを提供する
utilityヘッダーファイルは小さいですが、提供されるツールは非常に実用的です:
| コンポーネント/関数 | 用途 | 使用シーン |
|---|---|---|
std::pair |
2つの関連する値を格納する | 関数が複数の値を返す、mapの要素 |
std::make_pair |
pairを便利に作成する | テンプレート型推論、コードの簡素化 |
std::swap |
2つの値を交換する | アルゴリズム実装、ソート操作 |
std::move |
ムーブセマンティクスを有効にする | リソース管理、パフォーマンス最適化 |
std::forward |
完全転送 | ユニバーサル参照、テンプレートプログラミング |
コアコンポーネントの詳細
std::pair:キーと値のペアコンテナ
std::pairであるutilityライブラリで最もよく使われるコンポーネントで、2つの値を1つのオブジェクトにまとめるために使用します。
基本構文
#include <utility> // 创建 pair 对象的基本方式 std::pair<类型1, 类型2> 变量名(值1, 值2);
例
#include <iostream>
#include <utility>
#include <string>
int main() {
// 方法1:直接初期化
std::pair<int, std::string> student1(101, "Alice");
// 方法2:make_pair関数を使用(推奨)
auto student2 = std::make_pair(102, "Bob");
// 方法3:C++17以降でサポートされる推論ガイド
std::pair student3(103, "Charlie");
// pairのメンバーにアクセス
std::cout << "学籍番号: " << student1.first << ", 氏名: " << student1.second << std::endl;
return 0;
}
#include <utility>
#include <string>
int main() {
// 方法1:直接初期化
std::pair<int, std::string> student1(101, "Alice");
// 方法2:make_pair関数を使用(推奨)
auto student2 = std::make_pair(102, "Bob");
// 方法3:C++17以降でサポートされる推論ガイド
std::pair student3(103, "Charlie");
// pairのメンバーにアクセス
std::cout << "学籍番号: " << student1.first << ", 氏名: " << student1.second << std::endl;
return 0;
}
pairの一般的な操作
例
#include <utility>
#include <iostream>
void pairOperations() {
// pairを作成
std::pair<int, double> p1(10, 3.14);
std::pair<int, double> p2(20, 2.71);
// 比較操作
if (p1 < p2) {
std::cout << "p1 は p2 より小さい" << std::endl;
}
// 代入操作
p1 = p2;
// C++11以降でサポートされるメンバーごとの代入
int a;
double b;
std::tie(a, b) = p1; // p1の値をaとbにそれぞれ代入
// C++17の構造化束縛(より簡潔)
auto [x, y] = p1;
std::cout << "x = " << x << ", y = " << y << std::endl;
}
#include <iostream>
void pairOperations() {
// pairを作成
std::pair<int, double> p1(10, 3.14);
std::pair<int, double> p2(20, 2.71);
// 比較操作
if (p1 < p2) {
std::cout << "p1 は p2 より小さい" << std::endl;
}
// 代入操作
p1 = p2;
// C++11以降でサポートされるメンバーごとの代入
int a;
double b;
std::tie(a, b) = p1; // p1の値をaとbにそれぞれ代入
// C++17の構造化束縛(より簡潔)
auto [x, y] = p1;
std::cout << "x = " << x << ", y = " << y << std::endl;
}
std::make_pair:便利な作成関数
std::make_pairテンプレート関数であり、型を自動的に推論してpairの作成プロセスを簡素化します。
例
#include <utility>
#include <iostream>
#include <string>
void demonstrateMakePair() {
// 自動型推論により、テンプレート引数を明示的に指定する必要はありません
auto p1 = std::make_pair(42, "Hello");
auto p2 = std::make_pair(3.14, true);
// コンテナ内で使用すると特に便利です
std::vector<std::pair<int, std::string>> students;
students.push_back(std::make_pair(101, "Alice"));
students.push_back(std::make_pair(102, "Bob"));
for (const auto& student : students) {
std::cout << "学籍番号: " << student.first
<< ", 氏名: " << student.second << std::endl;
}
}
#include <iostream>
#include <string>
void demonstrateMakePair() {
// 自動型推論により、テンプレート引数を明示的に指定する必要はありません
auto p1 = std::make_pair(42, "Hello");
auto p2 = std::make_pair(3.14, true);
// コンテナ内で使用すると特に便利です
std::vector<std::pair<int, std::string>> students;
students.push_back(std::make_pair(101, "Alice"));
students.push_back(std::make_pair(102, "Bob"));
for (const auto& student : students) {
std::cout << "学籍番号: " << student.first
<< ", 氏名: " << student.second << std::endl;
}
}
実用ツール関数
std::swap:2つの値を交換する
std::swap同じ型の2つのオブジェクトの値を交換するために使用します。
例
#include <utility>
#include <iostream>
void demonstrateSwap() {
int a = 10, b = 20;
std::cout << "交換前: a = " << a << ", b = " << b << std::endl;
std::swap(a, b);
std::cout << "交換後: a = " << a << ", b = " << b << std::endl;
// カスタム型にも使用できます(ムーブセマンティクスを実装している場合)
std::string str1 = "Hello", str2 = "World";
std::swap(str1, str2);
std::cout << "文字列交換: " << str1 << " " << str2 << std::endl;
}
#include <iostream>
void demonstrateSwap() {
int a = 10, b = 20;
std::cout << "交換前: a = " << a << ", b = " << b << std::endl;
std::swap(a, b);
std::cout << "交換後: a = " << a << ", b = " << b << std::endl;
// カスタム型にも使用できます(ムーブセマンティクスを実装している場合)
std::string str1 = "Hello", str2 = "World";
std::swap(str1, str2);
std::cout << "文字列交換: " << str1 << " " << str2 << std::endl;
}
std::move:ムーブセマンティクスのサポート
std::moveオブジェクトを右辺値参照に変換して、ムーブセマンティクスを有効にするために使用します。
例
#include <utility>
#include <iostream>
#include <vector>
void demonstrateMove() {
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2;
std::cout << "移動前 - v1サイズ: " << v1.size()
<< ", v2サイズ: " << v2.size() << std::endl;
// ムーブセマンティクスを使用してリソースの所有権を移転する
v2 = std::move(v1);
std::cout << "移動後 - v1サイズ: " << v1.size()
<< ", v2サイズ: " << v2.size() << std::endl;
// v1は有効だが未定義の状態になります
// 通常、再代入しない限りv1を使用すべきではありません
}
#include <iostream>
#include <vector>
void demonstrateMove() {
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2;
std::cout << "移動前 - v1サイズ: " << v1.size()
<< ", v2サイズ: " << v2.size() << std::endl;
// ムーブセマンティクスを使用してリソースの所有権を移転する
v2 = std::move(v1);
std::cout << "移動後 - v1サイズ: " << v1.size()
<< ", v2サイズ: " << v2.size() << std::endl;
// v1は有効だが未定義の状態になります
// 通常、再代入しない限りv1を使用すべきではありません
}
std::forward:完全転送
std::forward完全転送を実装し、引数の値カテゴリを保持するために使用します。
例
#include <utility>
#include <iostream>
// 通常の関数 - 値カテゴリを保持できない
template<typename T>
void normalFunction(T arg) {
std::cout << "通常の関数引数" << std::endl;
}
// 完全転送を使用する関数
template<typename T>
void perfectForwardingFunction(T&& arg) {
// 引数の元の値カテゴリ(左辺値または右辺値)を保持する
normalFunction(std::forward<T>(arg));
}
void demonstrateForward() {
int x = 10;
// 左辺値を渡す
perfectForwardingFunction(x);
// 右辺値を渡す
perfectForwardingFunction(20);
}
#include <iostream>
// 通常の関数 - 値カテゴリを保持できない
template<typename T>
void normalFunction(T arg) {
std::cout << "通常の関数引数" << std::endl;
}
// 完全転送を使用する関数
template<typename T>
void perfectForwardingFunction(T&& arg) {
// 引数の元の値カテゴリ(左辺値または右辺値)を保持する
normalFunction(std::forward<T>(arg));
}
void demonstrateForward() {
int x = 10;
// 左辺値を渡す
perfectForwardingFunction(x);
// 右辺値を渡す
perfectForwardingFunction(20);
}
整数シーケンスツール(C++14)
C++14では、主にテンプレートメタプログラミングで使用される整数シーケンス関連のツールが導入されました。
std::integer_sequence
例
#include <utility>
#include <iostream>
// 整数シーケンスを使用してシーケンス内の各値を出力する
template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>) {
// 畳み込み式(C++17)を使用してすべての値を出力する
((std::cout << Ints << " "), ...);
std::cout << std::endl;
}
void demonstrateIntegerSequence() {
// 整数シーケンスを作成
auto seq = std::integer_sequence<int, 1, 2, 3, 4, 5>();
print_sequence(seq);
// make_integer_sequenceを使用してシーケンスを生成
auto seq2 = std::make_integer_sequence<int, 5>();
print_sequence(seq2); // 出力: 0 1 2 3 4
}
#include <iostream>
// 整数シーケンスを使用してシーケンス内の各値を出力する
template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>) {
// 畳み込み式(C++17)を使用してすべての値を出力する
((std::cout << Ints << " "), ...);
std::cout << std::endl;
}
void demonstrateIntegerSequence() {
// 整数シーケンスを作成
auto seq = std::integer_sequence<int, 1, 2, 3, 4, 5>();
print_sequence(seq);
// make_integer_sequenceを使用してシーケンスを生成
auto seq2 = std::make_integer_sequence<int, 5>();
print_sequence(seq2); // 出力: 0 1 2 3 4
}
実際の応用例
例1:関数が複数の値を返す
例
#include <utility>
#include <iostream>
#include <cmath>
// 関数が複数の値を返す:計算結果とエラーコード
std::pair<double, bool> calculateSqrt(double number) {
if (number < 0) {
return std::make_pair(0.0, false); // エラーケース
}
return std::make_pair(std::sqrt(number), true); // 成功ケース
}
void multipleReturnValues() {
auto result1 = calculateSqrt(16.0);
if (result1.second) {
std::cout << "平方根: " << result1.first << std::endl;
} else {
std::cout << "計算エラー: 負の数は平方根を求められません" << std::endl;
}
auto result2 = calculateSqrt(-4.0);
if (!result2.second) {
std::cout << "計算エラー: 負の数は平方根を求められません" << std::endl;
}
}
#include <iostream>
#include <cmath>
// 関数が複数の値を返す:計算結果とエラーコード
std::pair<double, bool> calculateSqrt(double number) {
if (number < 0) {
return std::make_pair(0.0, false); // エラーケース
}
return std::make_pair(std::sqrt(number), true); // 成功ケース
}
void multipleReturnValues() {
auto result1 = calculateSqrt(16.0);
if (result1.second) {
std::cout << "平方根: " << result1.first << std::endl;
} else {
std::cout << "計算エラー: 負の数は平方根を求められません" << std::endl;
}
auto result2 = calculateSqrt(-4.0);
if (!result2.second) {
std::cout << "計算エラー: 負の数は平方根を求められません" << std::endl;
}
}
例2:STLコンテナでの応用
例
#include <utility>
#include <map>
#include <iostream>
#include <string>
void mapWithPair() {
// std::mapの各要素はstd::pairです
std::map<int, std::string> studentMap;
// キーと値のペアを挿入
studentMap.insert(std::make_pair(101, "Alice"));
studentMap.emplace(102, "Bob"); // より効率的な方法
// mapを走査
for (const auto& [id, name] : studentMap) {
std::cout << "学籍番号: " << id << ", 氏名: " << name << std::endl;
}
// 要素を検索
auto it = studentMap.find(101);
if (it != studentMap.end()) {
std::cout << "学生を見つけました: " << it->second << std::endl;
}
}
#include <map>
#include <iostream>
#include <string>
void mapWithPair() {
// std::mapの各要素はstd::pairです
std::map<int, std::string> studentMap;
// キーと値のペアを挿入
studentMap.insert(std::make_pair(101, "Alice"));
studentMap.emplace(102, "Bob"); // より効率的な方法
// mapを走査
for (const auto& [id, name] : studentMap) {
std::cout << "学籍番号: " << id << ", 氏名: " << name << std::endl;
}
// 要素を検索
auto it = studentMap.find(101);
if (it != studentMap.end()) {
std::cout << "学生を見つけました: " << it->second << std::endl;
}
}
例3:簡単な辞書の実装
例
#include <utility>
#include <vector>
#include <iostream>
#include <algorithm>
class SimpleDictionary {
private:
std::vector<std::pair<std::string, std::string>> entries;
public:
void addWord(const std::string& word, const std::string& meaning) {
entries.emplace_back(word, meaning);
}
std::pair<bool, std::string> findMeaning(const std::string& word) {
for (const auto& [w, m] : entries) {
if (w == word) {
return std::make_pair(true, m);
}
}
return std::make_pair(false, "");
}
void printAll() {
for (const auto& [word, meaning] : entries) {
std::cout << word << ": " << meaning << std::endl;
}
}
};
void dictionaryExample() {
SimpleDictionary dict;
dict.addWord("apple", "ある果物");
dict.addWord("book", "読書に使う物体");
auto result = dict.findMeaning("apple");
if (result.first) {
std::cout << "意味: " << result.second << std::endl;
}
}
#include <vector>
#include <iostream>
#include <algorithm>
class SimpleDictionary {
private:
std::vector<std::pair<std::string, std::string>> entries;
public:
void addWord(const std::string& word, const std::string& meaning) {
entries.emplace_back(word, meaning);
}
std::pair<bool, std::string> findMeaning(const std::string& word) {
for (const auto& [w, m] : entries) {
if (w == word) {
return std::make_pair(true, m);
}
}
return std::make_pair(false, "");
}
void printAll() {
for (const auto& [word, meaning] : entries) {
std::cout << word << ": " << meaning << std::endl;
}
}
};
void dictionaryExample() {
SimpleDictionary dict;
dict.addWord("apple", "ある果物");
dict.addWord("book", "読書に使う物体");
auto result = dict.findMeaning("apple");
if (result.first) {
std::cout << "意味: " << result.second << std::endl;
}
}
ベストプラクティスと注意点
1. autoを使用してpair作成を簡素化する
例
// 推奨:autoとmake_pairを使用
auto student = std::make_pair(101, "Alice");
// 非推奨:型を明示的に指定(より冗長)
std::pair<int, std::string> student(101, "Alice");
auto student = std::make_pair(101, "Alice");
// 非推奨:型を明示的に指定(より冗長)
std::pair<int, std::string> student(101, "Alice");
2. insertではなくemplaceを優先する
例
std::map<int, std::string> myMap;
// 推奨:emplaceを使用(より効率的)
myMap.emplace(1, "one");
// 非推奨:insertを使用(一時オブジェクトの構築が必要)
myMap.insert(std::make_pair(1, "one"));
// 推奨:emplaceを使用(より効率的)
myMap.emplace(1, "one");
// 非推奨:insertを使用(一時オブジェクトの構築が必要)
myMap.insert(std::make_pair(1, "one"));
3. ムーブセマンティクスを正しく使用する
例
std::string createString() {
std::string str = "とても大きな文字列";
// 正しい:返却時にムーブを使用
return str; // コンパイラが自動的に最適化するため、明示的なmoveは不要
}
void processString(std::string str) {
// 文字列を処理
}
void usageExample() {
std::string largeStr = "とても大きなデータ";
// 正しい:受け渡し時にムーブを使用
processString(std::move(largeStr));
// 注意:largeStrは以後使用すべきではありません
}
std::string str = "とても大きな文字列";
// 正しい:返却時にムーブを使用
return str; // コンパイラが自動的に最適化するため、明示的なmoveは不要
}
void processString(std::string str) {
// 文字列を処理
}
void usageExample() {
std::string largeStr = "とても大きなデータ";
// 正しい:受け渡し時にムーブを使用
processString(std::move(largeStr));
// 注意:largeStrは以後使用すべきではありません
}
4. 構造化束縛の使用
例
// C++17以降でサポートされる構造化束縛
std::pair<int, std::string> getStudent() {
return {101, "Alice"};
}
void structuredBindingExample() {
// 従来の方法
auto student = getStudent();
int id = student.first;
std::string name = student.second;
// 現代の方法(C++17)
auto [id2, name2] = getStudent(); // より簡潔で明確
}
std::pair<int, std::string> getStudent() {
return {101, "Alice"};
}
void structuredBindingExample() {
// 従来の方法
auto student = getStudent();
int id = student.first;
std::string name = student.second;
// 現代の方法(C++17)
auto [id2, name2] = getStudent(); // より簡潔で明確
}