关键字
constexpr
- 扩展constexpr使用范围,可作用与if和lambda表达式
template <bool ok>
constexpr void func()
{
constexpr if(ok)
{
cout << "ok";
}
else
{
cout << "not ok";
}
}
constexpr auto fnSub = [](int a, int b)
{
return a + b;
}
fnSub(10, 20);
static_assert
- 扩展static_assert用法,静态断言的显示文本可选
static_assert(true, ""); static_assert(true);
auto
- 扩展auto的推断范围,在c++ 11 中,auto不能定义数组。现在c++ 17 可以。
auto x1 = { 1, 2 }; //推断出std::initializer_list<int>类型 auto x2 = { 1, 2.0 }; //错误:类型不统一,无法推断 auto x3{ 1, 2 }; //错误:auto的聚合初始化只能一个元素 auto x4 = { 3 }; //推断出std::initializer_list<int>类型 auto x5{ 3 }; //推断出int类型
typename
template <template<typename T> typename V>
struct test
{
V<double> y;
};
inline
- 扩展inline用法,可用于定义内联变量,C++17之前在头文件定义变量时需用extern导出,现在只需要加个inline
inline int a = 10;
u8
- 添加在常量字符串前,表示这是一个utf-8编码的字符串:
char str[] = u8"你好,世界!";
语法
折叠表达式
- 用于变长参数模板的解包,只支持各种运算符(和操作符)
template <typename ...T>
auto SubRight(T... args)
{
return (args - ...);
}
template <typename ...T>
auto SubLeft(T... args)
{
return (... - args);
}
SubLeft(9, 8, 7, 6, 5);
SubRight(9, 8, 7, 6, 5);
结构化绑定
- 依然是auto关键字,用一对包含一个或多个变量的中括号,表示结构化绑定
struct test
{
int a;
double b;
};
using testList = std::vector<test>
test func(int a, double b)
{
float x = a * a;
size_t y = b * b;
return [x, y];
}
auto [x, y] = fucn(10, 13.14);
for(auto [x, y] : testList)
允许非类型模板参数进行常量计算
class a
{
public:
static int b;
};
template <typename T>
auto func(T b)
{
return b;
}
auto ret = func<int>(a::b);
条件分支语句初始化
std::vector<int> v = { 1, 2, 3, 5, 4 };
if(auto itr = v.begin(); itr != v.end())
{
...
}
聚合初始化
- 在C++17之前,结构体使用大括号初始化需写构造函数,现在不需要了
struct x
{
int a;
float b;
};
struct y : x
{
std::string c;
};
y z = { { 10, 20 }, "hello world" };
y z{ { 10, 20 }, "hello world" };
嵌套命名空间
namespace a
{
namespace b
{
namespace c
{}
}
}
namespace a::b::c
{
}
lambda表达式捕获*this的值
struct test
{
int m_val;
int func()
{
return [*this]{ return this->m_val; };
}
};
细化表达式的计算顺序
- 为了支持泛型编程和重载运算符的广泛使用,新特性将计算顺序进行的细化
std::map<int, int> tmp;
tmp[0] = tmp.size();
为了解决该情况,新计算顺序规则为: ①后缀表达式从左到右求值。这包括函数调用和成员选择表达式。 ②赋值表达式从右向左求值。这包括复合赋值。 ③从左到右计算移位操作符的操作数。
模板类的模板参数自动推导
- 模板实例化时可不用显式指定类型,保证类型可以推导是必要条件
std::piar p(2, 3.14);
auto作为非类型模板参数
template <auto T>
void func(T t)
{
std::cout << t;
}
func<100>();
func<int>();
__has_include
#if __has_include(<stdio.h>)
printf("has");
#elif __has_include(<iostream>)
std::cout << "has";
#endif
属性
[fallthrough]
switch(0)
{
case 0:
....
[[fallthrough]];
case 1:
....
}
[nodiscard]
[[nodiscard]] int func(int a, int b)
{
return a + b;
}
[maybe_unused]
- 用于解除对未使用变量或其他引起的编译器警告:
[[maybe_unused]] int a;
库函数
std::from_chars & std::to_chars
#include <charconv>
#include <iostream>
int main()
{
std::string str("1234");
int val;
if(auto[ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), val);
ec == std::errc())
{
std::cout << val;
}
val = 321;
if(auto[ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), val);
ec == std::errc())
{
std::cout << str;
}
auto format = std::chars_format::general;
double pi = 0.f;
str = "3.14";
if(auto[ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), pi, format);
ec == std::errc())
{
std::cout << pi;
}
return 0;
}
std::variant
#include <variant>
#include <iostream>
int main()
{
std::variant<int, std::string> var("123");
std::variant<int, std::string> v("hello");
std::cout << v.index();
v = 123;
std::cout << v.index();
v = "321";
try
{
std::string str = std::get<std::string>(v);
int i = std::get<0>(v);
}
catch (std::bad_variant_access& e)
{
}
return 0;
}
std::optional
- 它的内部只有2个状态,要么有值(T类型),要么无值(nullopt)
#include <iostream>
#include <optional>
#include <string>
std::optional<size_t> func(int a)
{
if(a == 0)
return 1;
else if(a > 100)
return 2;
return std::nullopt;
}
int main()
{
std::optional<size_t> opt = std::nullopt;
opt.has_value();
opt.value();
opt = func(0);
opt.has_value();
opt.value();
return 0;
}
std::any
#include <iostream>
#include <any>
#include <string>
int main()
{
std::any var;
var.has_value();
std::cout << var.type().name();
var = 1;
std::cout << var.type().name();
var = 1.f;
std::cout << var.type().name();
var = "string";
std::cout << var.type().name();
return 0;
}
std::apply
int func(int a, int b){ return a + b; }
auto fn = [](int a, int b, int c){ return a * b + c; };
std::apply(func, std::pair(2, 3));
std::apply(fn , std::tuple(2, 3, 4));
std::make_from_tuple
struct a
{
int _a;
std::string _b;
a(int a, std::string b) : _a(a), _b(b){}
};
auto tuple = std::make_tuple(10, "hello");
auto a1 = std::make_from_tuple<a>(std::move(tuple ));
std::string_view
- 这个新的类型出现是为了解决string效率问题,它的内部只会保存一个char*指针,不会申请内存和销毁内存,如果你只需要读这个变量内部的数据而不写入的话,可以使用它
void func(std::string_view str)
{
std::cout << str;
}
std::string str("hello");
char str1[] = " world";
func(str);
func(str1);
std::as_const
int main()
{
std::string str("hello world");
cout << std::is_const<decltype(str)>::value << endl;
auto str_const = std::as_const(str);
cout << std::is_const<decltype(str_const)>::value << endl;
return 0;
}
std::filesystem
#include <iostream>
#include <filesystem>
#include <string>
namespace fs = std::filesystem;
int main()
{
fs::path file("./1");
if (!fs::exists(file))
{
std::cout << "file is not exist";
}
fs::directory_entry entry(file);
if (entry.status().type() == fs::file_type::directory)
{
std::cout << "it's a directory.";
}
fs::directory_iterator iter(file);
for (auto& path : iter)
{
std::cout << path.path().filename();
}
getchar();
return 0;
}
std::shared_mutex
class SafeThread
{
public:
SafeThread() = default;
private:
mutable std::shared_mutex _mutex;
size_t _val = 0;
public:
size_t get()const
{
std::shared_lock lock(_mutex);
return _val;
}
void add()
{
std::unique_lock lock(_mutex);
++_val;
}
void reset()
{
std::unique_lock lock(_mutex);
_val = 0;
}
}
|