Menu
CoddyTech

optional、variant、any

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 89/104。

Modern C++ では、<optional>、<variant>、<any> ヘッダーに、値が存在しない可能性がある場合、複数の型のいずれかを保持する場合、またはあらゆる型を格納する場合に役立つ 3 つのボキャブラリ型が用意されています。

std::optional<T>は、存在する場合も存在しない場合もある値を表します。例外をスローせずに失敗する可能性のある関数に最適です。

#include <iostream>
#include <optional>
std::optional<int> findIndex(const std::string& str, char c) {
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == c) return i;
    }
    return std::nullopt;  // 値なし
}
int main() {
    auto result = findIndex("hello", 'l');
    if (result.has_value()) {
        std::cout << "Found at: " << *result << "\n";  // 2
    }
}

std::variant<Types...>は、指定された型のうち常にちょうど1つを保持する型安全な共用体です。

#include <iostream>
#include <variant>
#include <string>
int main() {
    std::variant<int, double, std::string> data;
    data = 42;
    std::cout << std::get<int>(data) << "\n";
    data = "hello";
    if (std::holds_alternative<std::string>(data)) {
        std::cout << std::get<std::string>(data) << "\n";
    }
}

std::any は、実行時に決定される任意の型の値を保持できます。値を取得するには std::any_cast を使用します。

#include <iostream>
#include <any>
int main() {
    std::any value = 10;
    std::cout << std::any_cast<int>(value) << "\n";
    value = std::string("text");
    std::cout << std::any_cast<std::string>(value) << "\n";
}

NULL 許容値には std::optional を選び、コンパイル時に可能な型がわかっている場合は std::variant を使い、実行時の型の柔軟性が本当に必要な場合にのみ std::any を使用してください。

challenge icon

チャレンジ

簡単

現代の C++ のボキャブラリ型の力を示す設定システムを構築しましょう。値が存在しない可能性がある場合、異なる型を保持できる場合、または実行時に型を柔軟に扱う必要がある場合に対応できる、柔軟な設定マネージャーを作成します。

コードを 3 つのファイルに分けて整理します。

  • ConfigTypes.h: 設定値の型とヘルパー関数を定義します。

    文字列を受け取り、整数として解析を試みる parseValue という関数を作成します。文字列が有効な整数を表している場合は、std::optional でラップした整数を返します。解析に失敗した場合(文字列に数字以外の文字が含まれている場合)は、std::nullopt を返します。簡単にするため、文字列に数字のみ(先頭にマイナス記号を任意で含めることができます)が含まれている場合に有効とみなします。

    int、double、または std::string のいずれかを保持できる、std::variant を使用した Setting という型エイリアスも作成します。

    最後に、Setting を受け取り、保持している型とその値を次の形式で説明する文字列を返す describeSetting という関数を作成します。

    • int の場合: Integer: [value]
    • double の場合: Double: [value]
    • string の場合: String: [value]
  • DynamicStore.h: std::any を使用して、シンプルなキーと値のストアを作成します。

    任意の型の値を格納できる DynamicStore クラスを定義します。次の機能を持たせます。

    • 文字列キーと std::any 値を受け取り、それらを内部に格納する set メソッド(std::map を使用します)
    • キーを受け取り、std::any 値を返す get メソッド(キーが存在しない場合は空の std::any を返します)
    • キーが存在する場合に true を返す hasKey メソッド
  • main.cpp: 3 つの入力を読み取ります。
    1. 数値である可能性のある文字列(std::optional のテスト用)
    2. 型インジケーター: int、double、または string
    3. その型に対応する値

    3 つすべてのボキャブラリ型を実演します。

    まず、最初の入力に対して parseValue 関数を使用します。値が含まれている場合は、Parsed: [value] を出力します。空の場合は、Parse failed を出力します。

    次に、型インジケーターに基づいて Setting バリアントを作成します。型が int の場合は、値を整数として格納します。double の場合は、double として格納します。string の場合は、文字列として格納します。その後、describeSetting を呼び出して結果を出力します。

    最後に、DynamicStore を作成し、文字列 "config_loaded" をキー "status" の下に格納します。その後、それを取得して Status: [value] を出力します。値の抽出には std::any_cast<std::string> を使用します。

たとえば、入力が 42、int、100 の場合:

Parsed: 42
Integer: 100
Status: config_loaded

入力が hello、double、3.14 の場合:

Parse failed
Double: 3.14
Status: config_loaded

入力が -15、string、username の場合:

Parsed: -15
String: username
Status: config_loaded

必要な場所に適切なヘッダー(<optional>、<variant>、<any>、<string>、<map>)を忘れずにインクルードしてください。バリアントを扱うには std::holds_alternative と std::get を使用し、optional を確認するには has_value() または直接的な boolean 変換を使用します。

自分で試してみよう

#include <iostream>
#include <string>
#include <any>
#include "ConfigTypes.h"
#include "DynamicStore.h"

int main() {
    // 3つの入力を読み取る
    std::string input1;  // 数値かもしれない文字列(std::optionalのテスト用)
    std::string typeIndicator;  // 型インジケーター: int、double、またはstring
    std::string value;  // その型に対応する値
    
    std::cin >> input1;
    std::cin >> typeIndicator;
    std::cin >> value;
    
    // TODO: パート1 - parseValueでstd::optionalをテストする
    // input1でparseValue関数を使用する
    // 値が含まれている場合は "Parsed: [value]" を出力する
    // If it's empty, print "Parse failed"
    
    
    // TODO: パート2 - Settingでstd::variantをテストする
    // typeIndicatorに基づいてSettingバリアントを作成する
    // 型が "int" の場合、valueを整数として格納する
    // 型が "double" の場合、valueをdoubleとして格納する
    // 型が "string" の場合、valueを文字列として格納する
    // その後、describeSetting を呼び出して結果を出力する
    
    
    // TODO: Part 3 - DynamicStore で std::any をテストする
    // DynamicStore を作成する
    // キー "status" の下に文字列 "config_loaded" を格納する
    // それを取得して出力する: "Status: [value]"
    // 値を抽出するために std::any_cast<std::string> を使用する
    
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: C++オンラインコンパイラ