std::optional::optional(3)
π std::optional::optional(3) π
okay so we're in the C++ Standard Libary section (yes the page spells it like that, we do not fix, we preserve π«Ά), and today's main character is std::optional::optional β the constructor of everybody's favourite "maybe there's a value, maybe there isn't" type. bestie. this one is long. hydrate first. π
β¨ Synopsis β¨
constexpr optional() noexcept; (1) (since C++17)
constexpr optional( std::nullopt_t ) noexcept; (1) (since C++17)
constexpr optional( const optional& other ); (2) (since C++17)
constexpr optional( optional&& other )
noexcept(/* see below */); (3) (since C++17)
template < class U >
optional( const optional<U>& other ); (4) (until C++20)
(conditionally explicit)
template < class U >
constexpr optional( const optional<U>& other ); (4) (since C++20)
(conditionally explicit)
template < class U >
optional( optional<U>&& other ); (5) (until C++20)
(conditionally explicit)
template < class U >
constexpr optional( optional<U>&& other ); (5) (since C++20)
(conditionally explicit)
template< class... Args >
constexpr explicit optional( std::in_place_t, Args&&... args ); (6) (since C++17)
template< class U, class... Args >
constexpr explicit optional( std::in_place_t,
std::initializer_list<U> ilist,
Args&&... args ); (7) (since C++17)
template < class U = T >
constexpr optional( U&& value ); (8) (since C++17)
(conditionally explicit)
ngl the original page's column layout got absolutely obliterated by the typesetter π the numbers above are the ones the explanation list uses, so keep them glued to their constructors like they're your emotional support stickers. π
πΈ Description πΈ
Constructs a new optional object. that's the whole vibe. but how it constructs it? eight whole ways. let's go. π
1) Constructs an object that does not contain a value. (the "nope, empty, nothing in here" era β¨ β this covers both the constexpr optional() noexcept; and the constexpr optional( std::nullopt_t ) noexcept; forms.)
2) Copy constructor: If other contains a value, initializes the contained value as if direct-initializing (but not direct-list-initializing) an object of type T with the expression *other. If other does not contain a value, constructs an object that does not contain a value.
- This constructor is defined as deleted if
std::is_copy_constructible_v<T>isfalse. π - It is a trivial constructor if
std::is_trivially_copy_constructible_v<T>istrue.
3) Move constructor: If other contains a value, initializes the contained value as if direct-initializing (but not direct-list-initializing) an object of type T with the expression std::move(*other) and does not make other empty: a moved-from std::optional still contains a value, but the value itself is moved from. If other does not contain a value, constructs an object that does not contain a value.
this is the one people get wrong constantly, so say it with me: the optional isn't emptied, the value inside is the thing that got moved from. the box stays. the contents are haunted. π»
- This constructor does not participate in overload resolution unless
std::is_move_constructible_v<T>istrue. - It is a trivial constructor if
std::is_trivially_move_constructible_v<T>istrue.
4) Converting copy constructor: If other does not contain a value, constructs an optional object that does not contain a value. Otherwise, constructs an optional object that contains a value, initialized as if direct-initializing (but not direct-list-initializing) an object of type T with the expression *other.
- This constructor does not participate in overload resolution unless the following conditions are met:
std::is_constructible_v<T, const U&>istrue.- If
Tis not (possibly cv-qualified)bool,Tis not constructible or convertible from any expression of type (possibly const)std::optional<U>, i.e., the following 8 values are allfalse:std::is_constructible_v<T, std::optional<U>&>std::is_constructible_v<T, const std::optional<U>&>std::is_constructible_v<T, std::optional<U>&&>std::is_constructible_v<T, const std::optional<U>&&>std::is_convertible_v<std::optional<U>&, T>std::is_convertible_v<const std::optional<U>&, T>std::is_convertible_v<std::optional<U>&&, T>std::is_convertible_v<const std::optional<U>&&, T>
- This constructor is
explicitif and only ifstd::is_convertible_v<const U&, T>isfalse.
5) Converting move constructor: If other does not contain a value, constructs an optional object that does not contain a value. Otherwise, constructs an optional object that contains a value, initialized as if direct-initializing (but not direct-list-initializing) an object of type T with the expression std::move(*other).
- This constructor does not participate in overload resolution unless the following conditions are met:
std::is_constructible_v<T, U&&>istrue.- If
Tis not (possibly cv-qualified)bool,Tis not constructible or convertible from any expression of type (possibly const)std::optional<U>, i.e., the following 8 values are allfalse:std::is_constructible_v<T, std::optional<U>&>std::is_constructible_v<T, const std::optional<U>&>std::is_constructible_v<T, std::optional<U>&&>std::is_constructible_v<T, const std::optional<U>&&>std::is_convertible_v<std::optional<U>&, T>std::is_convertible_v<const std::optional<U>&, T>std::is_convertible_v<std::optional<U>&&, T>std::is_convertible_v<const std::optional<U>&&, T>
- This constructor is
explicitif and only ifstd::is_convertible_v<U&&, T>isfalse.
6) Constructs an optional object that contains a value, initialized as if direct-initializing (but not direct-list-initializing) an object of type T from the arguments std::forward<Args>(args)....
- If the selected constructor of
Tis aconstexprconstructor, this constructor is aconstexprconstructor. - The function does not participate in the overload resolution unless
std::is_constructible_v<T, Args...>istrue.
7) Constructs an optional object that contains a value, initialized as if direct-initializing (but not direct-list-initializing) an object of type T from the arguments ilist, std::forward<Args>(args)....
- If the selected constructor of
Tis aconstexprconstructor, this constructor is aconstexprconstructor. - The function does not participate in the overload resolution unless
std::is_constructible_v<T, std::initializer_list<U>&, Args...>istrue.
8) Constructs an optional object that contains a value, initialized as if direct-initializing (but not direct-list-initializing) an object of type T with the expression std::forward<U>(value).
- If the selected constructor of
Tis aconstexprconstructor, this constructor is aconstexprconstructor. - This constructor does not participate in overload resolution unless the following conditions are met:
std::is_constructible_v<T, U&&>istrue.std::decay_t<U>(until C++20) /std::remove_cvref_t<U>(since C++20) is neitherstd::in_place_tnorstd::optional<T>.- If
Tis (possibly cv-qualified)bool,std::decay_t<U>(until C++20) /std::remove_cvref_t<U>(since C++20) is not a specialization ofstd::optional.
- This constructor is
explicitif and only ifstd::is_convertible_v<U&&, T>isfalse.
π¦ Parameters π¦
otherβ anotheroptionalobject whose contained value is copiedvalueβ value with which to initialize the contained valueargs...β arguments with which to initialize the contained valueilistβ initializer list with which to initialize the contained value
π Exceptions π
2) Throws any exception thrown by the constructor of T.
3) Throws any exception thrown by the constructor of T. Has the following noexcept specification:
noexcept(std::is_nothrow_move_constructible<T>::value)
4-8) Throws any exception thrown by the constructor of T.
π Deduction guides π
the original page literally just says the words Deduction guides and thenβ¦ nothing. empty. a whole std::nullopt of a section π the example below does use one though, so go peek at o6 if you're nosy. iykyk.
β Notes β
Before the resolution of LWG issue 3836, constructing an std::optional<bool> from std::optional<U> would select overload (8) instead of overloads (4,5) if U is not bool. This is because overloads (4,5) did not participate in overload resolution if T (bool in this case) can be constructed or converted from std::optional<U>, but std::optional::operator bool makes the conversion possible for any U.
As a result, the constructed std::optional<bool> always contains a value. That value is determined by whether the provided std::optional<U> object contains a value, rather than the bool value direct-initialized from the contained value:
std::optional<bool> op_false(false);
std::optional<int> op_zero(0);
std::optional<int> from_bool(op_false); // OK: contains 0 (initialized from false)
std::optional<bool> from_int(op_0); // DEFECT (LWG 3836): contains true
// because op_0 contains a value, even if
// initializing bool from that value gives false
π«Ά real talk: yes, the snippet above genuinely saysop_0while the declaration above it saysop_zeroβ we are copying the page character for character, not fixing it, that's the whole deal. the bug being described is LWG 3836, and this naming thing is a whole separate little gremlin. π
π₯ Example π₯
// Run this code
#include <iostream>
#include <optional>
#include <string>
int main()
{
std::optional<int> o1, // empty
o2 = 1, // init from rvalue
o3 = o2; // copy-constructor
// calls std::string( initializer_list<CharT> ) constructor
std::optional<std::string> o4(std::in_place, {'a', 'b', 'c'});
// calls std::string( size_type count, CharT ch ) constructor
std::optional<std::string> o5(std::in_place, 3, 'A');
// Move-constructed from std::string using deduction guide to pick the type
std::optional o6(std::string{"deduction"});
std::cout << *o2 << ' ' << *o3 << ' ' << *o4 << ' ' << *o5 << ' '
<< *o6 << '\n';
}
and the output is literally:
1 1 abc AAA deduction
π©Ή Defect reports
okay so these behavior-changing defect reports got applied retroactively to previously published C++ standards β history really said βwe fixed it laterβ π:
LWG 3836β applied toC++17. behavior as published: when constructing anstd::optional<bool>fromstd::optional<U>, the overload resolution would select overload (8) ifUis notbool. correct behavior: always selects the converting copy/move constructor in this case β¨P0602R4β applied toC++17. behavior as published: copy/move constructors might not be required to propagate triviality even if underlying constructor is trivial. correct behavior: required to propagate triviality π«ΆP2231R1β applied toC++20. behavior as published: converting constructors from anotherstd::optionalwas notconstexprwhile the required operations can be inC++20. correct behavior: madeconstexprπ₯
π See also
make_optionalβ creates an optional object (C++17) (function template)
π·οΈ Category:
conditionally noexcept
source/version: http://cppreference.com, version 2024.06.10, page std::optional::optional(3) β mwah, bye bestie π
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms