类模板 std::optional
管理一个可选的容纳值,便可以存在也能够不存在的值。ios
一种常见的 optional
使用状况是一个可能失败的函数的返回值。与其余手段,如 std::pair<T,bool> 相比, optional
良好地处理构造开销高昂的对象,并更加可读,由于它显式表达意图。安全
std::optional对象只是包含对象的内部内存加上一个布尔标志。所以,大小一般比包含的对象大一个字节。对象使用与所包含类型相同的对齐方式。然而,std::optional对象不单单是向值成员添加布尔标志功能的结构。例如,若是没有值,就不会为所包含的类型调用构造函数(所以,能够为对象提供没有值的默认状态)。支持Move语义。app
std::optional<>为任意类型的可空实例建模。实例能够是成员、参数或返回值。能够认为std::optional<>是一个包含0或1个元素的容器。dom
/** @file optionalT.cpp * @note All Right Reserved. * @brief * @author xor * @date 2019-11-2 * @note * @history * @warning */ #include <iostream> #include <optional> #include <string> // convert string to int if possible: std::optional<int> asInt(const std::string& s) { try { return std::stoi(s); } catch (...) { return std::nullopt; } } std::optional<int> asInt2(const std::string& s) { std::optional<int> ret; // initially no value try { ret = std::stoi(s); } catch (...) { } return ret; } int main() { for (auto s : { "42", " 077", "hello", "0x33" }) { // convert s to int and use the result if possible: std::optional<int> oi = asInt(s); if (oi) { std::cout << "convert '" << s << "' to int: " << *oi << "\n"; } else { std::cout << "can't convert '" << s << "' to int\n"; } } }
has_value()用来检查是否有返回值,若是有经过value()来获取。value()比操做符*更安全,由于没有值而调用该接口的话会抛出异常。操做符*只有你确认有值的状况下才能使用,不然程序会出现未定义行为。函数
注意,能够经过使用新的类型std::string_view来改进asInt()。spa
/** @file optionalT.cpp * @note All Right Reserved. * @brief * @author xor * @date 2019-11-2 * @note * @history * @warning */ #include <iostream> #include <string> #include <optional> class Name { private: std::string first; std::optional<std::string> middle; std::string last; public: Name(std::string f, std::optional<std::string> m, std::string l) : first{ std::move(f) }, middle{ std::move(m) }, last{ std::move(l) } { } friend std::ostream& operator << (std::ostream& strm, const Name& n) { strm << n.first << ' '; if (n.middle) { strm << n.middle.value() << ' '; } return strm << n.last; } }; int main() { Name n{ "Jim", std::nullopt, "Knopf" }; std::cout << n << '\n'; Name m{ "Donald", "Ervin", "Knuth" }; std::cout << m << '\n'; return 0; }
可选对象还使用<utility>中定义的对象std::in_place(类型为std::in_place_t)来初始化带有多个参数的可选对象的值(参见下面)。code
能够建立一个没有值的可选对象。在这种状况下,必须指定包含的类型:
std::optional<int> o1;
std::optional<int> o2(std::nullopt);对象
这里不会为所包含的类型调用任何构造函数。blog
能够传递一个值来初始化所包含的类型。根据推导指南,没必要指定所包含的类型,以下:接口
std::optional o3{42}; // deduces optional<int>
std::optional<std::string> o4{"hello"};
std::optional o5{"hello"}; // deduces optional<const char*>
要初始化一个具备多个参数的可选对象,必须建立该对象或将std::in_place添加为第一个参数(所包含的类型没法推断):
std::optional o6{std::complex{3.0, 4.0}};
std::optional<std::complex<double>> o7{std::in_place, 3.0, 4.0};
注意,第二种形式避免建立临时对象。经过使用这种形式,甚至能够传递初始化器列表和附加参数:
// initialize set with lambda as sorting criterion:
auto sc = [] (int x, int y)
{
return std::abs(x) < std::abs(y);
};
std::optional<std::set<int,decltype(sc)>> o8{std::in_place, {4, 8, -7, -2, 0, 5}, sc};
能够复制可选对象(包括类型转换):
std::optional o5{"hello"}; // deduces optional<const char*>
std::optional<std::string> o9{o5}; // OK
还有一个方便的函数make_optional<>(),它容许使用单个或多个参数初始化(不须要in_place参数)。像往常同样make……函数推导:
auto o10 = std::make_optional(3.0); // optional<double>
auto o11 = std::make_optional("hello"); // optional<const char*>
auto o12 = std::make_optional<std::complex<double>>(3.0, 4.0);
然而,注意,没有构造函数接受一个值并根据它的值来决定是使用值初始化一个可选值仍是使用nullopt。可使用操做符?:,例如:
std::multimap<std::string, std::string> englishToGerman;
...
auto pos = englishToGerman.find("wisdom");
auto o13 = pos != englishToGerman.end()? std::optional{pos->second}: std::nullopt;
o13初始化为std::optional<std::string>,这是因为类模板参数的推导std::optionalf(pos->second)。对于std::nullopt类模板参数推导不起做用,可是运算符?:在推导表达式的结果类型时也将其转换为这种类型。
string_view 是C++17所提供的用于处理只读字符串的轻量对象。这里后缀 view 的意思是只读的视图。
/** @file optionalT.cpp * @note All Right Reserved. * @brief * @author xor * @date 2019-11-2 * @note * @history * @warning */ #include <string> #include <functional> #include <iostream> //#include <optional> #include <experimental/optional>//试验阶段 using namespace std; // optional 可用做可能失败的工厂的返回类型 std::optional<std::string> create(bool b) { if(b) return "Godzilla"; else return {}; } // 能用 std::nullopt 建立任何(空的) std::optional auto create2(bool b) { return b ? std::optional<std::string>{"Godzilla"} : std::nullopt; } // std::reference_wrapper 可用于返回引用 auto create_ref(bool b) { static std::string value = "Godzilla"; return b ? std::optional<std::reference_wrapper<std::string>>{value} : std::nullopt; } int main() { std::cout << "create(false) returned " << create(false).value_or("empty") << '\n'; // 返回 optional 的工厂函数可用做 while 和 if 的条件 if (auto str = create2(true)) { std::cout << "create2(true) returned " << *str << '\n'; } if (auto str = create_ref(true)) { // 用 get() 访问 reference_wrapper 的值 std::cout << "create_ref(true) returned " << str->get() << '\n'; str->get() = "Mothra"; std::cout << "modifying it changed it to " << str->get() << '\n'; } }
#include <iostream> #include <optional> #include <string_view> using namespace std; optional<size_t> find_last(string_view string, char to_find, optional<size_t> start_index = nullopt) { if (string.empty()) return nullopt; size_t index = start_index.value_or(string.size() - 1); while (true) { if (string[index] == to_find) return index; if (index == 0) return nullopt; --index; } } int main() { const auto string = "Growing old is mandatory; growing up is optional."; const optional<size_t> found_a{ find_last(string, 'a') }; if (found_a) cout << "Found the last a at index " << *found_a << endl; const auto found_b{ find_last(string, 'b') }; if (found_b) cout << "Found the last b at index " << found_b.value() << endl; const auto found_early_i(find_last(string, 'i', 10)); if (found_early_i != nullopt) cout << "Found an early i at index " << *found_early_i << endl; }