Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. C++ Dictionary
  3. optional

optional

Introduced in C++17, std::optional represents a state where a value "may or may not exist" in a type-safe way. It replaces traditional patterns like "returning -1 on failure" or "returning nullptr", making intent clear. Also from C++17, std::variant is a type-safe union that holds exactly one of several listed types and can be used with std::visit for pattern-matching style processing.

Syntax

#include <optional>
#include <string>

// optional variable declaration (no initial value = no value state)
std::optional<std::string> name;           // No value (equivalent to std::nullopt)
std::optional<int>         score = 42;     // Has a value

// Assign no-value state
name = std::nullopt;

// Check and retrieve the value
if (name.has_value()) {
    std::cout << name.value() << std::endl;
}

// Can also check with operator bool
if (score) {
    std::cout << *score << std::endl;  // Retrieve with *
}

// Returns a default value if no value is present
std::string displayName = name.value_or("unnamed");

// ========================================
// std::variant
// ========================================

#include <variant>

// variant that holds either int or std::string
std::variant<int, std::string> data = 100;
data = std::string("Unit-01");

// Check the current type
if (std::holds_alternative<std::string>(data)) {
    std::cout << std::get<std::string>(data) << std::endl;
}

// Process with std::visit
std::visit([](auto&& val) {
    std::cout << val << std::endl;
}, data);

Member / Function Reference

Syntax / FunctionDescription
std::optional<T>Wrapper type that holds a value of type T, or a no-value state (std::nullopt).
has_value()Returns bool indicating whether a value is held.
value()Returns the held value. Throws std::bad_optional_access if no value is present.
value_or(default)Returns the held value if present, otherwise returns the default argument.
operator bool / operator*Use if (opt) to check for a value, and *opt to retrieve it.
reset()Destroys the held value and enters the no-value state.
std::variant<T1, T2, ...>Type-safe union that always holds exactly one of the listed types.
std::holds_alternative<T>(v)Returns bool indicating whether variant v currently holds type T.
std::get<T>(v)Retrieves the value of type T from variant v. Throws std::bad_variant_access if the type is different.
std::get<N>(v)Retrieves the value as the type at index N.
std::visit(visitor, v)Automatically calls the overload corresponding to the current type of the variant.
v.index()Returns the index (0-based) of the type currently held in the variant.

Sample Code

eva_optional.cpp
#include <iostream>
#include <optional>
#include <string>
#include <vector>

// Returns the last mission for a pilot
// Returns std::nullopt if no sortie record exists
std::optional<std::string> getLastMission(const std::string& pilotName) {
    if (pilotName == "Ikari Shinji") {
        return "18th Angel: Kaworu battle (Unit-02 support)";
    } else if (pilotName == "Ayanami Rei") {
        return "16th Angel: Armisael battle (Unit-00 sortie)";
    } else if (pilotName == "Katsuragi Misato") {
        return "Strategic command for the 17th Angel";
    }
    return std::nullopt;
}

void displayMission(const std::string& pilotName) {
    std::optional<std::string> mission = getLastMission(pilotName);

    if (mission.has_value()) {
        std::cout << pilotName << "'s last sortie: " << mission.value() << std::endl;
    } else {
        std::cout << pilotName << " has no sortie record." << std::endl;
    }

    std::string display = mission.value_or("(no record)");
    std::cout << "  Summary: " << display << std::endl;
}

int main() {
    std::cout << "=== Pilot Sortie Records ===" << std::endl << std::endl;

    std::vector<std::string> pilots = {
        "Ikari Shinji",
        "Ayanami Rei",
        "Katsuragi Misato",
        "Nagisa Kaworu",      // No sortie record
        "Asuka Langley"       // No sortie record
    };

    for (int i = 0; i < (int)pilots.size(); i++) {
        displayMission(pilots[i]);
        std::cout << std::endl;
    }

    // Using operator bool and operator* shorthand
    std::cout << "=== Shorthand ===" << std::endl;
    std::optional<int> syncRate = 141;

    if (syncRate) {
        std::cout << "Shinji's sync rate: " << *syncRate << "%" << std::endl;
    }

    syncRate.reset();
    std::cout << "After sync graph loss: "
              << syncRate.value_or(0) << "%" << std::endl;

    return 0;
}
g++ -std=c++17 eva_optional.cpp -o eva_optional && ./eva_optional
=== Pilot Sortie Records ===

Ikari Shinji's last sortie: 18th Angel: Kaworu battle (Unit-02 support)
  Summary: 18th Angel: Kaworu battle (Unit-02 support)

Ayanami Rei's last sortie: 16th Angel: Armisael battle (Unit-00 sortie)
  Summary: 16th Angel: Armisael battle (Unit-00 sortie)

Katsuragi Misato's last sortie: Strategic command for the 17th Angel
  Summary: Strategic command for the 17th Angel

Nagisa Kaworu has no sortie record.
  Summary: (no record)

Asuka Langley has no sortie record.
  Summary: (no record)

=== Shorthand ===
Shinji's sync rate: 141%
After sync graph loss: 0%
eva_variant.cpp
#include <iostream>
#include <variant>
#include <string>
#include <vector>

using EvaData = std::variant<int, std::string, double>;

struct EvaDataPrinter {
    void operator()(int val) const {
        std::cout << "[Code]   " << val << std::endl;
    }
    void operator()(const std::string& val) const {
        std::cout << "[Name]   " << val << std::endl;
    }
    void operator()(double val) const {
        std::cout << "[Output] " << val << " MW" << std::endl;
    }
};

int main() {
    std::cout << "=== Eva Spec Data (variant) ===" << std::endl << std::endl;

    std::vector<EvaData> specs = {
        0,
        std::string("Ayanami Rei"),
        17500.0,
        1,
        std::string("Ikari Shinji"),
        28000.0,
    };

    for (int i = 0; i < (int)specs.size(); i++) {
        std::visit(EvaDataPrinter{}, specs[i]);
    }

    std::cout << std::endl;

    std::cout << "=== Type Check and Value Retrieval ===" << std::endl;

    EvaData pilotData = std::string("Nagisa Kaworu");

    if (std::holds_alternative<std::string>(pilotData)) {
        std::cout << "Pilot: " << std::get<std::string>(pilotData) << std::endl;
    }

    pilotData = 5;
    std::cout << "Index: " << pilotData.index() << std::endl;
    std::cout << "Code: "  << std::get<int>(pilotData) << std::endl;

    std::cout << std::endl << "=== Catching bad_variant_access ===" << std::endl;
    try {
        std::string s = std::get<std::string>(pilotData);  // pilotData is int, not string
    } catch (const std::bad_variant_access& e) {
        std::cout << "Exception caught: " << e.what() << std::endl;
    }

    return 0;
}
g++ -std=c++17 eva_variant.cpp -o eva_variant && ./eva_variant
=== Eva Spec Data (variant) ===

[Code]   0
[Name]   Ayanami Rei
[Output] 17500 MW
[Code]   1
[Name]   Ikari Shinji
[Output] 28000 MW

=== Type Check and Value Retrieval ===
Pilot: Nagisa Kaworu
Index: 0
Code: 5

=== Catching bad_variant_access ===
Exception caught: std::bad_variant_access

Common mistake 1: Calling value() without checking for a value

Calling value() on a std::optional that holds no value throws std::bad_optional_access. Always check for a value before calling it.

// NG: Calling value() without checking throws an exception
std::optional<std::string> mission = std::nullopt;
std::string m = mission.value();  // Throws bad_optional_access

OK: Check with has_value() first, or use value_or().

// OK: Check with has_value() before calling value()
if (mission.has_value()) {
    std::cout << mission.value() << std::endl;
}

// OK: Use value_or() to specify a default value
std::string display = mission.value_or("(no record)");

Overview

std::optional<T> is a C++17 type representing "a value that may or may not exist." Using it as a return value replaces conventional patterns like -1, nullptr, or empty strings to represent errors or unset values. Check for a value with has_value() or operator bool; retrieve it with value(), operator*, or value_or(). Calling value() with no value present throws std::bad_optional_access, so always check first with has_value() or use value_or(). std::variant<T1, T2, ...> is a type-safe union that always holds exactly one of the listed types. Use std::holds_alternative<T> to check the type and std::get<T> to retrieve the value. Passing a visitor (an object with operator() overloads for each type) to std::visit automatically calls the overload for the current type, ensuring no type case is missed. Both std::optional and std::variant require C++17 or later with the -std=c++17 option. See also: Exception Handling / auto and Type Deduction / tuple / pair.

If you find any errors or copyright issues, please .