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吧