c++17之std::optional,std::variant以及std::any

in_place用法

c++中有三个类似于c语言中union一样的类型,分别为

  • std::optional< T> --持有T或者什么都没有
  • std::variant–持有T或者U等
  • std::any–持有任意类型

std::optional

cpp-reference optional
类模板 std::optional 管理一个可选的容纳值,既可以存在也可以不存在的值。可以有值或者nullopt。一般用作返回值,

具体常用的函数看如下代码

#include 
#include 
#include 
#include 
#include 
using namespace std;

class T
{
    public:
    T(int i):a(i){cout<<"the value is "<<i<<endl;}
    T(T&& t){cout<<"this is move"<<endl;}
    T(const T& t)=default;
    friend ostream& operator<<(ostream &os, T &t);
    private:
    int a;
};

ostream& operator<<(ostream &os,T &t)
{
    os<<t.a;
    return os;
}
optional<string> fun1(bool b)
{
    // return b? "hello":nullopt; // : liangbian bixuyao leixing yizhi ,qianzhe shi const char* houzhe bushi 
    if(b)
        return "hello";
    else
        return {};
}

auto fun2(bool b)
{
    return b? optional<string>("world"):nullopt;
}

auto fun3(bool b,optional<string> c)
{
    return b? optional<reference_wrapper<string>>(c.value()):nullopt;
}
int main()
{
    cout<<fun1(false).value_or("????")<<endl;  //value_or(...)为返回optional的值,如果没有返回...
    auto mv1 = fun2(true);
    auto mv2 = fun3(true,mv1.value());
    cout<<mv2->get()<<endl;    // world
    mv2->get() = "is";
    cout<<mv2->get()<<endl;    // is

    auto opt = make_optional<vector<T>>(3,1); // make_optional使用
    
    optional<vector<T>> opt1(in_place,vector<T>{4,3});// 这里与vector的emplace_back实质一致,可以穿args也可以传Tp的对象,如果是args,则在插入点构造,如果是传构造好的对象,则要move
    optional<T> optt1(in_place,T(4));  //这里看下面log,打印出了the value is 4与this is move,说明先构造再move
    optional<vector<T>> opt2(in_place,4,3);   //使用in_place为了取代emplace,即在插入点构造,与上面少了move
    
    cout << opt2.value().size() << endl;  //4
    opt2.emplace(3,4);  //emplace用法,在插入点构造,同时会取代原来的值
    cout << opt2.value().size() << endl;  //3
    for(auto a:*opt2){cout<<a<<endl;}  //*opt2,为解引用

    opt2.reset();  //若 *this 含值,则如同用 value().T::~T() 销毁此值。否则无效果
    if(!opt2.has_value())  //has_value为判断是否有值
        cout<<"std::bad_optional_access"<<endl;


}
????
world
is
the value is 1
the value is 4
the value is 3
the value is 4
this is move
the value is 3
4
the value is 4
3
4
4
4
std::bad_optional_access

std::variant

cpp reference_std::variant
类模板 std::variant 表示一个类型安全的联合体。 std::variant 的一个实例在任意时刻要么保有其一个可选类型之一的值,要么在错误情况下无值(此状态难以达成,见 valueless_by_exception )。

其主要的用法如下所示

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class T
{
    public:
    T(int i):a(i){cout<<"the value is "<<i<<endl;}
    T(T&& t){this->a = t.a;cout<<"this is move"<<endl;}
    T(const T& t)=default;
    friend ostream& operator<<(ostream &os, T &t);
    private:
    int a;
};

ostream& operator<<(ostream &os,T &t)
{
    os<<t.a;
    return os;
}

int main()
{



    variant<int,T,float> var1;
    var1 = 1.2f;    //直接用=初始化
    cout<<"the variant is "<<get<float>(var1)<<endl;  // 1.2 
    try {
        get<int>(var1); // var1 含 float 而非 int :将抛出
    }
    catch (const std::bad_variant_access&) {}
    //cout<<"the variant is "<(var1)<
    var1 = 2;
    cout<<"the variant is "<<get<int>(var1)<<endl;  //2
    cout<<"the variant is "<<get<0>(var1)<<endl;  // get<0>(...) == get(...)


    variant<int,T,float> var2(in_place_type<T>,3);
    cout<<"the variant is "<<get<T>(var2)<<endl;   //std::get<>()

    variant<int,T,float> var3(in_place_type<T>,T(4)); //move
    cout<<"the variant is "<<get<1>(var3)<<endl;

    var3.emplace<2>(1.2f);
    var3.emplace<1>(2);  //这里注意与optional的emplace的区别,需要加定位
    cout<<"the variant is "<<get<1>(var3)<<endl;  // 2
    cout<<var3.index()<<endl; // index == 1

    using var_t = std::variant<int, long, double, std::string>;
    std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
    for(auto v:vec){
        visit([](auto&& args){std::cout << args<<endl;},v);
        visit([](auto&& args){
        using m_type = decay_t<decltype(args)>; //remove cv
        if constexpr (is_same_v<m_type,int>){cout<<"the variant value is "<<args<<endl;} //if后跟constexpr为编译时判断
        else if constexpr (is_same_v<m_type,long>){cout<<"the variant value is "<<args<<endl;}
        else if constexpr (is_same_v<m_type,double>){cout<<"the variant value is "<<args<<endl;}
        else if constexpr (is_same_v<m_type,string>){cout<<"the variant value is "<<args<<endl;}
    },v);
    }


}
the variant is 1.2
the variant is 2
the variant is 2
the value is 3
the variant is 3
the value is 4
this is move
the variant is 4
the value is 2
the variant is 2
1
10
the variant value is 10
15
the variant value is 15
1.5
the variant value is 1.5
hello
the variant value is hello

这里需要注意的是std::visit,配合variant来使用,前者输入为仿函数或者lambda,后者为variant,实现函数对variant的处理

template <class Visitor, class... Variants>
constexpr /*see below*/ visit(Visitor&& vis, Variants&&... vars);
(1)	(C++17)
template <class R, class Visitor, class... Variants>
constexpr R visit(Visitor&& vis, Variants&&... vars);
#include 
#include 
#include 
 
struct MyVisitor
{
    void operator()(double d) const {
        std::cout << d << '\n';
    }
    void operator()(int i) const {
        std::cout << i << '\n';
    }
    void operator()(const std::string& s) const {
        std::cout << s << '\n';
}
};

int main()
{
    std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");
 
    std::visit(MyVisitor(), var1); // calls operator() for matching int type 
    std::visit([](auto&& a){std::cout<<a<<std::endl;}, var3); 
 
    return 0;
}
42
visit

std::any

cpp_reference_std::any
类 any 描述用于任何类型的单个值的类型安全容器。

  1. 类 any 的对象存储任何满足构造函数要求的类型的一个实例或为空,而这被称为 any 类对象的状态。存储的实例被称作所含对象。若两个状态均为空,或均为非空且其所含对象等价,则两个状态等价。
  2. 非成员 any_cast 函数提供对所含对象的类型安全访问。
#include 
#include 
#include 
 using namespace std;
class T
{
    public:
    T(int i):a(i){cout<<"the value is "<<i<<endl;}
    T(T&& t){this->a = t.a;cout<<"this is move"<<endl;}
    T(const T& t)=default;
    friend ostream& operator<<(ostream &os, T &t);
    private:
    int a;
};

int main()
{
    std::any a = 1;  //any赋值,然后需要使用时,再any_cast,之前需要判断是否has_value
    cout<<a.has_value()<<endl;  // true
    cout<<a.type().name()<<" : "<<any_cast<int>(a)<<endl;
    try
    {
        a = 1;
        std::cout << std::any_cast<float>(a) << '\n';
    }
    catch (const std::bad_any_cast& e)
    {
        std::cout << e.what() << '\n';
    }
    auto b = make_any<T>(123);
    cout<<b.type().name()<<endl; //1T 
    b.reset();
    b.emplace<T>(234);

    a = 1;
    int* i = std::any_cast<int>(&a);// any_cast(指针)实现指针的类型转换,只能转换any值
    std::cout << *i << "\n";


    return 0;
}
1
i : 1
bad any_cast
the value is 123
1T
the value is 234
1

你可能感兴趣的:(c++学习,c++,开发语言,后端)