Namespaces
Variants

std::optional<T>::optional

来自 zh.cppreference.net
 
 
 
 
constexpr optional() noexcept;
constexpr optional( std::nullopt_t ) noexcept;
(1) (C++17 起)
constexpr optional( const optional& other );
(2) (C++17 起)
constexpr optional( optional&& other ) noexcept(/* see below */);
(3) (C++17 起)
template < class U >
optional( const optional<U>& other );
(4) (C++17 起)
(C++20 起为 constexpr)
(有条件地显式)
template < class U >
optional( optional<U>&& other );
(5) (C++17 起)
(C++20 起为 constexpr)
(有条件地显式)
template< class... Args >
constexpr explicit optional( std::in_place_t, Args&&... args );
(6) (C++17 起)
template< class U, class... Args >
constexpr explicit optional( std::in_place_t,
                             std::initializer_list<U> ilist,
                             Args&&... args );
(7) (C++17 起)
template < class U = std::remove_cv_t<T> >
constexpr optional( U&& value );
(8) (C++17 起)
(有条件地显式)

构造一个新的 optional 对象。

1) 构造一个不含值的对象。
2) 复制构造函数:如果 other 包含值,则初始化所含值,如同直接初始化(但不是直接列表初始化)一个类型为 T 的对象,其初始化器为表达式 *other。如果 other 不包含值,则构造一个不含值的对象。
  • 如果 std::is_copy_constructible_v<T> 是 false,则此构造函数被定义为弃置的。
  • 如果 std::is_trivially_copy_constructible_v<T> 是 true,则它是平凡构造函数。
3) 移动构造函数:如果 other 包含值,则初始化所含值,如同直接初始化(但不是直接列表初始化)一个类型为 T 的对象,其初始化器为表达式 std::move(*other),并且不会使 other 为空:被移动后的 std::optional 仍包含一个值,但该值本身已被移动。如果 other 不包含值,则构造一个不含值的对象。
  • 除非 std::is_move_constructible_v<T> 是 true,否则此构造函数不参与重载决议。
  • 如果 std::is_trivially_move_constructible_v<T> 是 true,则它是平凡构造函数。
4) 转换复制构造函数:如果 other 不包含值,则构造一个不含值的 optional 对象。否则,构造一个包含值的 optional 对象,该值初始化方式如同直接初始化(但不是直接列表初始化)一个类型为 T 的对象,其初始化器为表达式 *other。
  • 此构造函数仅在满足以下条件时参与重载决议:
    • std::is_constructible_v<T, const U&> 是 true。
    • 如果 T 不是(可能有 cv 限定)bool,则 T 不能从任何类型为(可能有 const)std::optional<U> 的表达式构造或转换,即 converts_from_any_cvref<T, std::optional<U>> 是 false,其中 converts_from_any_cvref 如下所述。
  • 此构造函数是 explicit,当且仅当 std::is_convertible_v<const U&, T> 是 false。
5) 转换移动构造函数:如果 other 不包含值,则构造一个不含值的 optional 对象。否则,构造一个包含值的 optional 对象,该值初始化方式如同直接初始化(但不是直接列表初始化)一个类型为 T 的对象,其初始化器为表达式 std::move(*other)。
  • 此构造函数仅在满足以下条件时参与重载决议:
    • std::is_constructible_v<T, U&&> 是 true。
    • 如果 T 不是(可能有 cv 限定)bool,则 T 不能从任何类型为(可能有 const)std::optional<U> 的表达式构造或转换,即 converts_from_any_cvref<T, std::optional<U>> 是 false,其中 converts_from_any_cvref 如下所述。
  • 此构造函数是 explicit,当且仅当 std::is_convertible_v<U&&, T> 是 false。
6) 构造一个包含值的 optional 对象,该值初始化方式如同直接初始化(但不是直接列表初始化)一个类型为 T 的对象,参数为 std::forward<Args>(args)...。
  • 如果 T 的被选构造函数是 constexpr 构造函数,则此构造函数是 constexpr 构造函数。
  • 除非 std::is_constructible_v<T, Args...> 是 true,否则该函数不参与重载决议。
7) 构造一个包含值的 optional 对象,该值初始化方式如同直接初始化(但不是直接列表初始化)一个类型为 T 的对象,参数为 ilist, std::forward<Args>(args)...。
  • 如果 T 的被选构造函数是 constexpr 构造函数,则此构造函数是 constexpr 构造函数。
  • 除非 std::is_constructible_v<T, std::initializer_list<U>&, Args...> 是 true,否则该函数不参与重载决议。
8) 构造一个包含值的 optional 对象,该值初始化方式如同 直接初始化(但不是直接列表初始化)一个类型为 T 的对象,其初始化器为表达式 std::forward<U>(value)。
  • 如果 T 的被选构造函数是 constexpr 构造函数,则此构造函数是 constexpr 构造函数。
  • 此构造函数仅在满足以下条件时参与重载决议:
    • std::is_constructible_v<T, U&&> 是 true。
    • std::decay_t<U>(C++20 前)std::remove_cvref_t<U>(C++20 起) 既不是 std::in_place_t 也不是 std::optional<T>。
    • 如果 T 是(可能有 cv 限定)bool,则 std::decay_t<U>(C++20 前)std::remove_cvref_t<U>(C++20 起) 不是 std::optional 的特化。
  • 此构造函数是 explicit,当且仅当 std::is_convertible_v<U&&, T> 是 false。

辅助变量模板 converts_from_any_cvref 等价于:

template< class T, class W >
constexpr bool converts_from_any_cvref =
    std::disjunction_v<std::is_constructible<T, W&>, std::is_convertible<W&, T>,
                       std::is_constructible<T, W>, std::is_convertible<W, T>,
                       std::is_constructible<T, const W&>, std::is_convertible<const W&, T>,
                       std::is_constructible<T, const W>, std::is_convertible<const W, T>>;

参数

other - 另一个 optional 对象,其包含的值将被复制
value - 用于初始化所包含值的值
args... - 用于初始化所包含值的参数
ilist - 用于初始化所包含值的初始化列表

异常

3) 抛出 T 的构造函数抛出的任何异常。
4) 抛出 T 的构造函数抛出的任何异常。具有以下
noexcept 规范:
noexcept ( std:: is_nothrow_move_constructible < T > :: value )
5-9) 抛出由 T 的构造函数抛出的任何异常。

注释

在 LWG 问题 3836 解决之前,从 std:: optional < U > 构造 std:: optional < bool > 时,若 U 不是 bool 类型,会选择重载 ( 9 ) 而非重载 ( 5,6 ) 。这是因为当 T (此处为 bool )可从 std:: optional < U > 构造或转换时,重载 ( 5,6 ) 不参与重载决议,而 std::optional::operator bool 使得这种转换对任意 U 皆成立。

因此,构造出的 std:: optional < bool > 始终包含一个值。该值的确定依据是提供的 std:: optional < U > 对象是否包含值,而非基于所含值直接初始化得到的 bool 值:

std::optional<bool> op_false(false);
std::optional<int> op_zero(0);
std::optional<int> from_bool(op_false); // 正确:包含值0(从false初始化)
std::optional<bool> from_int(op_zero);  // 缺陷(LWG 3836):包含true,因为
                                        // op_zero包含有效值,即使从该值
                                        // 初始化bool类型会得到false
功能测试 宏 值 标准 功能特性
__cpp_lib_optional 202106L (C++20)
(DR20)
完全 constexpr 化 ( 5,6 )

示例

#include <iostream>
#include <optional>
#include <string>
int main()
{
    std::optional<int> o1, // 空值
                       o2 = 1, // 从右值初始化
                       o3 = o2; // 拷贝构造函数
    // 调用 std::string( initializer_list<CharT> ) 构造函数
    std::optional<std::string> o4(std::in_place, {'a', 'b', 'c'});
    // 调用 std::string( size_type count, CharT ch ) 构造函数
    std::optional<std::string> o5(std::in_place, 3, 'A');
    // 使用推导指南从 std::string 移动构造
    std::optional o6(std::string{"deduction"});
    std::cout << *o2 << ' ' << *o3 << ' ' << *o4 << ' ' << *o5  << ' ' << *o6 << '\n';
}

输出:

1 1 abc AAA deduction

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 应用于 发布时的行为 正确行为
LWG 3836 C++17 当从 std:: optional < U > 构造 std:: optional < bool > 时,
若 U 不是 bool 则会选择重载 ( 9 )
在此情况下始终选择
转换复制/移动构造函数
LWG 3886 C++17 重载 ( 9 ) 的默认模板参数为 T 改为 std:: remove_cv_t < T >
P0602R4 C++17 即使底层构造函数是平凡的,
复制/移动构造函数也可能不是平凡的
要求传播
平凡性
P2231R1 C++20 来自其他 std::optional 的重载 ( 5,6 ) 不是 constexpr 改为 constexpr

参见

创建 optional 对象
(函数模板)