cpp17 开坑

写了一年多的C代码,最近又重拾C++,打算看看cpp17~cpp20新增的特性,安装了VS2022 Community版本(终于支持x64)

1.variant

variant on cppreference
在Windows平台接触过COM编程的都知道,ATL提供了CComVariant实现了类似功能,缺点也很多(不能跨平台,与标准库结合用起来太变扭,尤其是编码转换)。很多开源库也早就实现了类似的功能,这里就不一一细说了。

Demo

#include <string>
#include <variant>
#include <iostream>

using VariantType = std::variant<int, double, float, long, std::string, std::wstring>;

int main(int argc, char* argv[])
{
    //初始化Variant类型
    VariantType vInt = 5;
    VariantType vString = string("5");

    //访问Variant数据
    auto valInt = std::get<int>(vInt);
    auto &valString = std::get<std::string>(vString);

    return 0;
}

动态转换

以前做引擎需要用到类似的多态能力去描述各种不同的变量类型,基本套路就是 引入type成员变量 + union,cpp17的variant也用了类似的技术。值得学习的是variant通过模板实现,用了index代表了模板参数类型的索引,基类定义了index成员变量,每次模板类variant初始化时在编译期初始化index。
并且提供了多种方式获取动态类型,代码如下:

VariantType v = 5;

//1. try catch + get
try {
     std::get<float>(v); // w contains int, not float: will throw
}
catch (const std::bad_variant_access& ex) {
    std::cout << ex.what() << '\n';
}

//2. get_if
if(const int* pval = std::get_if<int>(&v))
    std::cout << "variant value: " << *pval << '\n'; 
else 
    std::cout << "failed to get value!" << '\n'; 

//3. visit
std::visit([](auto&& arg){std::cout << arg;}, v);

//4. visit + decltype + is_same
std::visit([](auto&& arg) {
     using T = std::decay_t<decltype(arg)>;
     if constexpr (std::is_same_v<T, int>)
          std::cout << "int with value " << arg << '\n';
}, v);

//5. index
constexpr int VTInt = VariantType(int(0)).index();
if(v.index() == VTInt)
    std::cout << "variant value: " << VTInt << '\n'; 
else 
    std::cout << "failed to get value!" << '\n'; 

其他要点

文档上已经说明了variant无法动态分配内存(隐式类型编译期绑定),模板类型无法包含 引用、数组(int[5]这类,vector等容器可以使用) 和 void ,可以使用std::variant<std::monostate>表示空类型。

2.any , optional

any可以用于描述任意可复制拷贝类型, optional实现上感觉跟variant区别不大(使用的语境不同),毕竟两者都继承自相同基类。optional可以为空,空值为std::nullopt。

Demo

#include <any>

struct AnyDemoSt
{
	int value1;
	double value2;

	AnyDemoSt() { value1 = 0; value2 = 0.1; }
	AnyDemoSt(const AnyDemoSt& val) { value1 = val.value1; value2 = val.value2; }
};

void TestAny()
{
	std::any obj = 0.0;
	std::cout << obj.type().name() << std::endl;

	std::any st = AnyDemoSt();
	std::cout << st.type().name() << std::endl;
}

3.to_chars, from_chars

to_chars用来把int, long, float, double等类型转换为字符串;from_chars则用来字符串转换为int, long, float, double等类型。用法跟cpp11的 strtol, itox类似吧。

4.filesystem

filesystem属实相见恨晚,跨平台的文件库各自标准不一,标准库的filesystem总体更像 unix 风格(文件权限 owner, group, others)。
功能还是比较齐全,支持文件访问权限、文件类型(包含socket、fifo所以Windows上只有部分支持)、文件操作(copy,move,rename等)。

5.byte

typedef unsigned char byte....

enum class byte : unsigned char {};

6.string_view

跟string相比,string_view不会动态分配内存,从构造函数来看,只是初始化了成员变量_MyData与_MySize(浅拷贝)。

#include <string_view>
std::string_view strView1 = "1234";
触发以下构造函数
    /* implicit */ constexpr basic_string_view(_In_z_ const const_pointer _Ntcts) noexcept // strengthened
        : _Mydata(_Ntcts), _Mysize(_Traits::length(_Ntcts)) {}

7.destroy launder

destroy = placement destroy(new)
launder看起来比较费劲,从官方文档主要是为了避免内存重用后的编译器优化行为(先调用destroy A, 再调用placement new B),告诉编译器launder后的内存指向新的对象。

8.其他

其他库方面似乎更新了一批数学库,std::gcd, std::lcm(求最大公约数和最小公倍数)等等。

总结下来能用的比较多的还是any/variant、fielsystem吧

posted @ 2022-09-14 23:27  宛若青空sakura  阅读(500)  评论(0编辑  收藏  举报