1. optional的作用

类模板 std::optional 管理一个可选的容纳值,即可以存在也可以不存在的值。

一种常见的 optional 使用情况是一个可能失败的函数的返回值。与其他手段,如 std::pair<T,bool> 相比, optional 良好地处理构造开销高昂的对象,并更加可读,因为它显式表达意图。

std::optional对象只是包含对象的内部内存加上一个布尔标志。因此,大小通常比包含的对象大一个字节。对象使用与所包含类型相同的对齐方式。然而,std::optional对象不仅仅是向值成员添加布尔标志功能的结构。例如,如果没有值,就不会为所包含的类型调用构造函数(因此,可以为对象提供没有值的默认状态)。支持Move语义。

2. optional使用

std::optional<>为任意类型的可空实例建模。实例可以是成员、参数或返回值。可以认为std::optional<>是一个包含0或1个元素的容器。

2.1 std::optional<>返回值

/** @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()
{
    ", "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()。

2.2 std::optional<>参数和数据成员

/** @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';

    ;
}

可选对象还使用<utility>中定义的对象std::in_place(类型为std::in_place_t)来初始化带有多个参数的可选对象的值(参见下面)。

3. optional构造函数

可以创建一个没有值的可选对象。在这种情况下,必须指定包含的类型:
std::optional<int> o1;
std::optional<int> o2(std::nullopt);

这里不会为所包含的类型调用任何构造函数。

可以传递一个值来初始化所包含的类型。根据推导指南,不必指定所包含的类型,如下:

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类模板参数推导不起作用,但是运算符?:在推导表达式的结果类型时也将其转换为这种类型。

4. string_view

string_view 是C++17所提供的用于处理只读字符串的轻量对象。这里后缀 view 的意思是只读的视图。

    • 通过调用 string_view 构造器可将字符串转换为 string_view 对象。
      string 可隐式转换为 string_view。
    • string_view 是只读的轻量对象,它对所指向的字符串没有所有权。
    • string_view通常用于函数参数类型,可用来取代 const char* 和 const string&。
      string_view 代替 const string&,可以避免不必要的内存分配。
    • string_view的成员函数即对外接口与 string 相类似,但只包含读取字符串内容的部分。
      string_view::substr()的返回值类型是string_view,不产生新的字符串,不会进行内存分配。
      string::substr()的返回值类型是string,产生新的字符串,会进行内存分配。
    • string_view字面量的后缀是 sv。(string字面量的后缀是 s)

5. 示例

/** @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();

    while (true)
    {
        if (string[index] == to_find) return index;
        ) 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;

    ));
    if (found_early_i != nullopt)
        cout << "Found an early i at index " << *found_early_i << endl;
}

C++17新特性optional和string_view的更多相关文章

  1. JDK1.8新特性——Optional类

    JDK1.8新特性——Optional类 摘要:本文主要学习了JDK1.8新增加的Optional类. 部分内容来自以下博客: https://www.cnblogs.com/1ning/p/9140 ...

  2. JAVA8新特性Optional,非空判断

    Optional java 的 NPE(Null Pointer Exception)所谓的空指针异常搞的头昏脑涨, 有大佬说过 "防止 NPE,是程序员的基本修养." 但是修养归 ...

  3. c++17 新特性

    编译环境说明:gcc 8.1 + eclipse +windows 10 eclipse cpp默认支持c++14,做c++17开发时,需要手动进行配置. 1.关键字 1)constexpr c++1 ...

  4. Java 17 新特性:switch的模式匹配(Preview)

    还记得Java 16中的instanceof增强吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); da ...

  5. C++17 新特性之 std::optional(上)

    最近在学习 c++ 17 的一些新特性,为了加强记忆和理解,把这些内容作为笔记记录下来,有理解不对的地方请指正,欢迎大家留言交流. 引言 在介绍之前,我们从一个问题出发,C++ 的函数如何返回多个值? ...

  6. Java8 新特性 Optional 类

    Optional 类的简介   Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常.   Optional ...

  7. Java8新特性--Optional

    Java 8引入了一个新的Optional类.Optional类的Javadoc描述如下: 这是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会 ...

  8. Java8新特性——Optional

    前言 在开发中,我们常常需要对一个引用进行判空以防止空指针异常的出现.Java8引入了Optional类,为的就是优雅地处理判空等问题.现在也有很多类库在使用Optional封装返回值,比如Sprin ...

  9. 从Java 9 到 Java 17 新特性梳理

    Java 9 新的创建集合的方法  // [1, 2, 3, 4]  List<Integer> integers = List.of(1, 2, 3, 4);  // {1,2,3}   ...

随机推荐

  1. 第二篇 -- C#中对XML操作

    一.XML文件操作中与.Net中对应的类 微软的.NET框架在System.xml命名空间提供了一系列的类用于Dom的实现. 以下给出XML文档的组成部分对应.NET中的类: XML文档组成部分 对应 ...

  2. 23、matplotlib数据可视化、绘图库模块

    matplotlib官方文档:https://matplotlib.org/contents.html?v=20190307135750 matplotlib是一个绘图库,它可以创建常用的统计图,包括 ...

  3. Oracle的去重函数 distinct

    原贴地址:http://www.cnblogs.com/rainman/archive/2013/05/03/3058451.html#m0 SQL中distinct的用法   1.作用于单列 2.作 ...

  4. Spark SQL中的Catalyst 的工作机制

      Spark SQL中的Catalyst 的工作机制 答:不管是SQL.Hive SQL还是DataFrame.Dataset触发Action Job的时候,都会经过解析变成unresolved的逻 ...

  5. 各位大神,我请教一个问题,我在Android studio上创一个project显示错误

    Error:FAILURE: Build failed with an exception. * Where: Build file 'C:\Users\Administrator\AndroidSt ...

  6. cf1158A-The Party and Sweets - (贪心+思维)

    题意:有n个男孩,m个女孩,每个男孩给每个女孩一堆糖果.b数组表示每个男孩给出的最少糖果数,g数组表示每个女孩子收到的最大糖果数.求所有男孩给出的最小糖果总数. 解题: 先对b数组和g数组从小到大排序 ...

  7. 第4章 Spring的数据库开发

    4.1 Spring JDBC Spring的JDBC模块负责数据库资源管理和错误处理,化简了开发者对数据库的操作. 4.11 Spring JdbcTemplate的解析 * JdbcTemplat ...

  8. day003-python初识

    基本的写代码流程:1.创建 xxx.py文件 注意:文件不要保存在中文的路径下,和文件名不要以中文命名. 2.写代码 a.注意两行文件头 #! /usr/bin/env python  # -*- c ...

  9. PHP字符串比较函数详解

    在PHP中,对于字符串之间的比较有很多种方法,第一种是使用 strcmp()函数和 strcasecmp()函数按照字节进行比较,第二种是使用 strnatcmp()函数按照自然排序法进行比较,第三种 ...

  10. map访问key不存在的情况下,用find。比[]直接访问的意思不一样,map[key]不返null

    key不存在的话则创建一个pair并调用默认构造函数 map<CGuid, CLibItem>::iterator iterItem = m_world->m_library_sce ...