類模板 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; }