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;
}

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;
}

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;
    }
}

実用ツール関数

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;
}

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を使用すべきではありません
}

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);
}

整数シーケンスツール(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
}

実際の応用例

例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;
    }
}

例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;
    }
}

例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;
    }
}

ベストプラクティスと注意点

1. autoを使用してpair作成を簡素化する

例

// 推奨:autoとmake_pairを使用
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"));

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は以後使用すべきではありません
}

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();  // より簡潔で明確
}
その他の拡張