std::optional<T>

Последнее обновление: 07.03.2023

Начиная с версии стандарта C++17 в стандартную библиотеку C++ был добавлен тип std::optional<T> (модуль optional), который позволяет избежать таких ситуаций, когда значение не найдено или не устнавлено, и определить для подобных ситуаций значение по умолчанию. Рассмотрим конкретную ситуацию, для чего он нужен.

Например, нам надо определить функцию для поиска индекса символа в строке. С одной стороны, все просто - перебираем строку посимвольно, находим нужный символ и возвращаем его индекс. С другой стороны, а что, если символ не будет найден? На этот случай мы могли бы предусмотреть возвращение некоторого значения по умолчанию. Наиболее часто используемым вариантом в данном случае является число -1, поскольку данное число не представляет действительный индекс. И получив его из функции, мы будем знать, что символ не найден.

Тип optional предоставляет альтернативный подход: если индекс найден, то он возвращается. Если значение не найдено, то возвращается константа std::nullopt, которая указывает, что значение optional не установлено.

Для примера определим следующую программу:

#include <iostream>
#include <string>
#include <optional>

std::optional<unsigned> find_index(const std::string&, char);
int main()
{
    const std::string text = "An apple a day keep the doctor away.";
    // находим индекс символа 'p'
    char p_char{'p'};
    const std::optional<unsigned> p_index{ find_index(text, p_char) };
    std::cout << "Index of p: " << *p_index << std::endl;
    
    // находим индекс символа 'b'
    char b_char{'b'};
    const std::optional<unsigned> b_index{ find_index(text, b_char) };
    std::cout << "Index of b: " << *b_index << std::endl;
}

std::optional<unsigned> find_index(const std::string& text, char c)
{
    // если пустая строка, возвращаем специальное значение std::nullopt
    if (text.empty())
        return std::nullopt; 
    // в цикле находим начальный индекс символа
    for(unsigned i{}; i < text.size();i++)
    {
        // если символ найден, возвращаем индекс символа
        if(text[i]==c)
        {
            return i;
        } 
    }
    // в остальных случаях возвращаем std::nullopt
    return std::nullopt;
}

Здесь определена функция find_index(), которая принимает text в виде константной ссылки на строку и символ для поиска и возвращает значение std::optional<unsigned>. Объект optional типизируется типом значений, которые он должен содержать. Поскольку индекс символа представляет целое беззнаковое число, то здесь типизируем optional типом unsigned. В самой функции, если символ не найден или строка пуста, то возвращаем значение std::nullopt. Таким образом, если индекс найден, то optional будет содержать найденный индекс. А если индекс не найден, то значение std::nullopt

В функции main ищем два символа: "p", который есть в исходном тексте, и "b", который отсутствует. Для поления значения из option можно использовать операцию *, например, *p_index. В итоге в данном случае мы получим следующий вывод:

Index of p: 4
Index of b: 4251975392

Так, мы видим, что поскольку символ "b" не найден, optional будет содержать довольно большое число, которое и представляет std::nullopt.

Пойдем дальше и изменим программу следующим образом:

#include <iostream>
#include <string>
#include <optional>

std::optional<unsigned> find_index(const std::string&, char); 
void print_index(std::optional<unsigned>, char);

int main()
{
    const std::string text = "An apple a day keep the doctor away.";
    // находим индекс символа 'p'
    char p_char{'p'};
    const std::optional<unsigned> p_index{ find_index(text, p_char) };
    print_index(p_index, p_char);
    
    // находим индекс символа 'b'
    char b_char{'b'};
    const std::optional<unsigned> b_index{ find_index(text, b_char) };
    print_index(b_index, b_char);
}
// выводим индекс на консоль, если символ найден
void print_index(std::optional<unsigned> index, char c)
{
    if(index)
        std::cout << "Index of "<< c << ": " << *index << std::endl;
    else
        std::cout << "Index of "<< c << " not found" << std::endl;
}
std::optional<unsigned> find_index(const std::string& text, char c)
{
    if (text.empty())
        return std::nullopt;
    for(unsigned i{}; i < text.size();i++)
    {
        if(text[i]==c)
        {
            return i;
        } 
    }
    return std::nullopt;
}

Здесь добавлена функция print_index(), которая выводит индекс найденного символа. При этом мы можем проверить значение optional:

if(index)

Если optional равен std::nullopt, то это условие возвратит false. Таким образом, мы можем проверить на наличие значения. Консольный вывод программы:

Index of p: 4
Index of b not found

Optional API

Тип optional также предоставляет ряд функций. Некоторые из них:

  • has_value(): возвращает true, если optional содержит значение.

    Так, в примере выше мы могли бы заменить проверку

    if(index)

    на следующую строку

    index.has_value()
  • value(): возвращает значение из optional. Так, в примере выше мы могли бы значение следующим образом

    if(index.has_value())
        std::cout << "Index of "<< c << ": " << index.value() << std::endl;
    else
        std::cout << "Index of "<< c << " not found" << std::endl;
    

    Единственное, что надо помнить, что перед вызовом этого метода следует проверять на наличие значения.

  • value_or(default): если optional содержит значение, то возвращает это значение. Если значение в optional отсутствует, то возвращает аргумент default, который передается в функцию

Рассмотрим применение последней функции, которая может использоваться в тех ситуациях, когда необходимо определить для параметров значение по умолчанию:

#include <iostream>
#include <optional>
 
double pow(double, std::optional<unsigned> = std::nullopt);
int main()
{
    double n1 = pow(4, 3);
    std::cout << n1 << std::endl;   // 64
    double n2 = pow(4);     // используем значение по умолчанию
    std::cout << n2 << std::endl;   // 16
}

double pow(double number, std::optional<unsigned> exp)
{
    unsigned a = exp.value_or(2);
    double result{1.0};
    for(unsigned i{}; i < a;i++)
    {
        result *= number;
    }
    return result;
}

Здесь функция pow принимает число, которое надо возвести в степень, и значение степени в виде std::optional<unsigned>. По умолчанию, если этому параметру не передано значение, то оно равно std::nullopt.

В самой функции проверяем это значение, и если оно НЕ установлено, то возвращаем число 2 (то есть число будет возводиться в квадрат):

unsigned a = exp.value_or(2);
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850