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 を使用してください。
チャレンジ
簡単現代の 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]
- int の場合:
DynamicStore.h:std::anyを使用して、シンプルなキーと値のストアを作成します。任意の型の値を格納できる
DynamicStoreクラスを定義します。次の機能を持たせます。- 文字列キーと
std::any値を受け取り、それらを内部に格納するsetメソッド(std::mapを使用します) - キーを受け取り、
std::any値を返すgetメソッド(キーが存在しない場合は空のstd::anyを返します) - キーが存在する場合に true を返す
hasKeyメソッド
- 文字列キーと
main.cpp: 3 つの入力を読み取ります。- 数値である可能性のある文字列(
std::optionalのテスト用) - 型インジケーター:
int、double、またはstring - その型に対応する値
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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス12現代的なC++の機能
ムーブセマンティクスと右辺値完全転送ラムダ式を深く理解するstd::functionとstd::bindconstexprとconsteval構造化束縛optional、variant、any自分で練習してみよう: C++オンラインコンパイラ