C++17好用的类

Posted 勇搏风浪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++17好用的类相关的知识,希望对你有一定的参考价值。

C++17:std::any std::variant std::optional

简介

  • std::any是一个类型安全的容器,可以放置各种类型的数据。
  • std::variant是类型安全的union
  • std::optional该类型是用来表示一个值是不是存在的。

std::any

场景:类型擦除,任意类型转化,类型安全
例子:

#include <any>
#include <iostream>

int main()

	std::cout << std::boolalpha; //将bool值用 "true" 和 "false"显示
	std::any a;  //定义一个空的any,即一个空的容器
	//有两种方法来判断一个any是否是空的
	std << cout << a.has_value() <<std::endl; // any是空的时,has_value 返回值为 false
	std << cout << a.type().name() <<std::endl; //any 是空的时,has_value 返回值为 true
	//几种创建any的方式
	std::any b = 1;                         //b 为存了int类型的值的any
	auto c = std::make_any<float>(5.0f);    //c为存了float类型的any
	std::any d(6.0);                        //d为存储了double类型的any
	std << cout << b.has_value() <<std::endl;   //true 
	std << cout << b.type().name() <<std::endl; //int
	std << cout << c.has_value() <<std::endl;   //true
	std << cout << c.type().name() <<std::endl; //float
	std << cout << d.has_value() <<std::endl;   //true
	std << cout << d.type().name() <<std::endl; //double

	//更改any的值    
	a = 2;   //直接重新赋值
	auto e = c.emplace<float>(4.0f); //调用emplace函数,e为新生成的对象引用

	//清空any的值
	b.reset();
	std << cout << b.has_value() <<std::endl;   //false
	std << cout << b.type().name() <<std::endl; //int

	//使用any的值
	try
	
		auto f = std::any_cast<int>(a); //f为int类型,其值为2
		std::cout << f <<std::endl; //2
	
	catch(const std::bad_any_cast& e)
	
		std::cout<< e.what()<<std::endl;
	

	try
	
		auto g = std::any_cast<float>(a); //抛出std::bad_any_cat 异常
		std::cout << g <<std::endl; //该语句不会执行
	
	catch(const std::bad_any_cast& e)
	
		std::cout<< e.what()<<std::endl;  //可能输出Bad any_cast
	
	return 0;

std::variant

场景:代替联合体,类型安全

#include <any>
#include <iostream>
#include <variant>

union my_union

    int i;
    float f;
    char c;
;

int main()

    std::cout << std::boolalpha;
    std::variant<int, float, char> variant;
    //这里的variant等价于my_union
    //在构造的时候,如果构造过程中抛出了异常,valueless_by_exception的返回值为true
    std::cout<< variant.valueless_by_exception()<<std::endl;   //false

    
        variant = 12;   // variant包含了int类型
        int i = std::get<int>(variant);  //使用std::get<T>可以获取所含有的值

        try
        
            auto f = std::get<float>(variant);  //此时的值为int,所以想要获取float的时候就会抛出异常
        
        catch (const std::bad_variant_access& exception)
        
            std::cout << exception.what() << std::endl;
        

        variant = 1.0f;
        auto f = std::get<float>(variant);
        std::cout << f << std::endl;  //1.0
    
    

    
        //还可以使用索引来获取对应的值
        auto f = std::get<1>(variant);  
        try
        
            auto i = std::get<0>(variant);
        
        catch (const std::bad_variant_access & exception)
        
            std::cout << exception.what() << std::endl;
        

        variant = 1;
        auto i = std::get<0>(variant);
        std::cout<<i<<std::endl;  //1
    

    variant = 2.0f;
    std::cout << variant.index() << std::endl;  //1

    variant = 2;
    std::cout << variant.index() << std::endl;  //0

    return 0;

std::optional

该类型是用来表示一个值是不是存在的。std::optional有两个状态,即有值和无值。通常我们将std::optional用于函数的返回值,当函数执行成功了返回有值的状态,当函数执行失败了返回无值的状态。当std::optional有值时,它可以在使用bool值的地方转化为true,反之,转化为false。

场景:
比如说有一个读取文件的函数,这个函数会把文件的内容,存储在一个string中,作为结果返回。因为读取可能会失败,所以这个函数有必要返回一个信息,来表明读取文件是否成功,如果用返回空字符串的方式来表示读取失败,不太好,因为文件可能本身就是空文件,所以空字符串不能代表读取失败。

例子:

#include <iostream>
#include <optional>

int main()

    std::cout << std::boolalpha;
    std::optional<int> op1;       //表示一个不存在的值
    std::optional<int> op2 = 1;   //表示一个存在的int类型的值
    std::optional<int> op3(2);    //表示存在的一个int类型的值
    std::optional<std::string> op4(std::in_place, 3, 'A');     //构建一个存有std::string类型的std::optional,
                                                               //调用std::string的构造函数,其值为"AAA"
    
    std::cout << op1.has_value() << std::endl;   //输出为false
    
    if (!op1)    
    
        std::cout << "empty optional is false" << std::endl;  //由于op1不存在值,所以在此转化为false
    

    try 
    
        std::cout << op4.value() << std::endl;     //获取std::optional 的值,如果不存在值,则抛出std::bad_optional_access异常
    
    catch (std::bad_optional_access& e)
    
        std::cout << e.what() << std::endl;
    

    std::optional<int> op5;
    std::cout<<op5.value_or(5)<<std::endl;

    op2.reset();
    std::cout << op2.has_value() << std::endl; //false

    op2 = 3;
    op2 = 4.0;
    std::cout << op2.value() << std::endl;    //4.0
    
    op2.emplace<int>(5);
    return 0;

总结

多使用C++标准库好用的工具

boost实用工具:创建一个禁止复制的类 noncopyable

  boost的noncopyable允许创建一个禁止复制的类,使用很简单,但很好用!

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
 
/* boost_noncopyable.cpp 创建一个禁止复制的类
    noncopyable允许程序轻松实现一个禁止复制的类;
*/


#include <iostream>
#include <boost/noncopyable.hpp> // 或#include <boost/utility.hpp>

using namespace std;
using namespace boost;

//在C++中定义一个类的时候,如果不明确定义拷贝构造函数和拷贝复制操作符,编译器会为我们自动生成
//但有时候我们不需要类的复制语义,希望禁止复制类的实例
//这是一个很经典的C++惯用语法,只要私有化拷贝构造函数和拷贝赋值操作函数即可
//如果程序中有大量这样的类,重复写这样的代码是相当乏味的,而且代码出现的次数越多越容易增加手写出错的几率
class empty_class
{
public:
    empty_class() {}
    ~empty_class() {}

    
//编译器默认添加拷贝构造函数和拷贝赋值函数
    empty_class(const empty_class & ) {}
    empty_class &
operator=(const empty_class &) {}
protected:
private:
};

class noncopy_class
{
public:
    noncopy_class() {}
    ~noncopy_class() {}
protected:
private:
    
//私有化拷贝构造函数和拷贝赋值函数,禁止复制类
    noncopy_class(const noncopy_class & ) {}
    noncopy_class &
operator=(const noncopy_class &) {}
};

//针对以上情况,boost中的noncopyable为实现不可拷贝类提供了简单清晰的解决方案
//从boost::noncopyable派生即可
/*
class noncopyable
{
protected:
#if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) && !defined(BOOST_NO_CXX11_NON_PUBLIC_DEFAULTED_FUNCTIONS)
    BOOST_CONSTEXPR noncopyable() = default;
    ~noncopyable() = default;
#else
    noncopyable() {}
    ~noncopyable() {}
#endif
#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
    noncopyable( const noncopyable& ) = delete;
    noncopyable& operator=( const noncopyable& ) = delete;
#else
private:  // emphasize the following members are private
    noncopyable( const noncopyable& );
    noncopyable& operator=( const noncopyable& );
#endif
};
}

typedef noncopyable_::noncopyable noncopyable;
*/

class do_not_copy_class : noncopyable
{
public:
protected:
private:
};

int main(void)
{
    empty_class em_c1;
    empty_class em_c2(em_c1);
    empty_class em_c3 = em_c1;

    noncopy_class noc_c1;
    
//error C2248: ‘noncopy_class::noncopy_class‘ : cannot access private member declared in class ‘noncopy_class‘
    //noncopy_class noc_c2(noc_c1) ;
    //noncopy_class noc_c3 = noc_c1;

    do_not_copy_class d1;
    
//error C2248: ‘boost::noncopyable_::noncopyable::noncopyable‘ : cannot access private member declared in class ‘boost::noncopyable_::noncopyable‘
    //do_not_copy_class d2(d1);
    //do_not_copy_class d3 = d1;

    
//只要有可能就使用boost::noncopyable,它明确无误地表达了类设计者的意图;
    //对用户更加友好,而且与其它的boost库配合的也好
    cin.get();
    
return 0;
}


























































































































































































以上是关于C++17好用的类的主要内容,如果未能解决你的问题,请参考以下文章

学习C语言,有哪些好用的编译器可以推荐吗?

java面试题-java基础

unity unitywebrequest 好用么

C语言编译器哪个好?几款好用的C语言编译器推荐

C语言,哪种编译器比较好用?

c++17好用的新特性总结