Optional values with std::optional

Lesson Introduction

Handling optional values is crucial in C++ to prevent runtime errors and undefined behavior. Imagine you're looking for a specific item in a list. If it isn't found, how do you handle that? Traditionally, you might use pointers or sentinel values, but these can be error-prone. The std::optional class template in C++17 offers a cleaner, type-safe alternative.

The goal of this lesson is to help you understand and use std::optional to handle values that may or may not be present. We'll cover its creation, usage, and practical applications with real examples.

Introduction to `std::optional`

So, what is std::optional? Think of it as a container that may or may not hold a value. It’s useful when a function might not always return a meaningful value. For example, if you try to find an item in a list and it's not there, instead of returning a null pointer or a special sentinel value, you can return an std::optional.

Creating Optional Values

Creating an std::optional value is simple:

#include <optional>
#include <iostream>

int main() {
    // An optional containing an integer value
    std::optional<int> val = 42; // Initialized with a value
    // An empty optional
    std::optional<int> emptyVal; // Initialized without a value (empty)
    return 0;
}

In this code snippet:

  • val is an std::optional initialized with the integer value 42.
  • emptyVal is an empty std::optional of type int.

Accessing Values in `std::optional`

Accessing the value contained in an std::optional can be done safely by first checking if it contains a value using the .has_value() member function. If the std::optional does contain a value, the .value() member function can be used to access the contained value. Here's an example to illustrate these concepts:

#include <optional>
#include <iostream>

int main() {
    std::optional<int> val = 42; // Initialized with a value

    // Check if val has a value
    if (val.has_value()) {
        // Access and print the value
        std::cout << "Value inside val: " << val.value() << std::endl; // Output: Value inside val: 42
    } else {
        std::cout << "val is empty" << std::endl;
    }

    std::optional<int> emptyVal; // Initialized without a value (empty)

    // Check if emptyVal has a value
    if (emptyVal.has_value()) {
        std::cout << "Value inside emptyVal: " << emptyVal.value() << std::endl;
    } else {
        std::cout << "emptyVal is empty" << std::endl; // Output: emptyVal is empty
    }

    return 0;
}

Using .value() without checking .has_value() will throw an exception if the std::optional is empty. To avoid this, always check with .has_value() before accessing the value.

Alternatively, you can access the value with the * operator. Instead of val.value(), you can use *val.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal