C++17新特性
C++17新特性
- C++17新特性
- 前言
- If Statements with Initializer
- Constexpr if
- inline 变量
- 嵌套命名空间
- 属性说明符
- string_view
- filesystem
- any
- optional
- variant
- execution
- 参考链接
前言
前项目
C++
使用的时C++14
标准,考虑到与时俱进,C++20
也基本上确定,不过由于目前使用的linux(uos)
上的自带gcc
并不能全面支持C++20
, 所以暂时考虑新项目上C++17
新标准,并且写一篇完整的C++17中项目可能用到的新特性简单文档
visual studio 2019
需要添加编译参数 /std:c++17
If Statements with Initializer
带初始化器的if
语句
example
std::map<std::string, int> map;
map["nihao"] = 1;
map["shijie"] = 2;
if (auto ret = map.begin(); ret != map.end()) {
std::cout << ret->first << ": " << ret->second;
}
Constexpr if
在 constexpr if
语句中,条件 的值必须是可按语境转换到 bool
类型的经转换常量表达式。若其值为 true
,则舍弃 false
分支语句(若存在),否则舍弃 true
分支语句。
通常我们写业务很难用到,在模板元编程中会特别有用
example
template <typename T>
void TestConstexprIf(T value) {
if constexpr (std::is_integral_v<T>)
std::cout << "is integral" << std::endl;
else
static_assert(false, "T必须是整型");
}
inline 变量
inline
变量用来解决一个问题,全局变量再头文件中定义之后多出使用产生符号重定义错误
错误例子
// test.h 头文件
int test = 10;
// test1.cpp
void Function1() {
test = 20;
}
// test2.cpp
void Function() {
test = 30;
}
// 上面的代码编译将会产生重定义错误,c++17之前解决方案是使用extern导出全局变量
// 解决方案
// test.h 头文件
extern int test;
// test.cpp
int test = 10;
C++17
之后引入inline
变量使其全局变量可以直接再头文件中声明定义
example
inline int test = 10;
嵌套命名空间
example
namespace test::test2 {
int i = 0;
}
std::cout << test::test2::i << std::endl;
属性说明符
属性为各种由实现定义的语言扩展(例如 GNU
与 IBM
的语言扩展 __attribute__((...))
,微软的语言扩展 __declspec()
等)提供了统一化的语法。
[[fallthrough]] 标准属性
指示从前一标号直落是有意的,而在发生直落时给出警告的编译器不应诊断它。
C++17
之前的标准下, 有如下代码
switch (device.status())
{
case sleep:
device.wake();
// fall thru
case ready:
device.run();
break;
case bad:
handle_error();
break;
}
C++17 可以这样写
switch (device.status())
{
case sleep:
device.wake();
[[fallthrough]];
case ready:
device.run();
break;
case bad:
handle_error();
break;
}
再之前的代码编译器会告诉你没有break
的警告,但是再c++17
中使用fallthrough
属性就可以消除这个警告了
[[maybe_unused]] 标准属性
可用来消除未使用的函数和变量编译器所发出的警告
example
[[maybe_unused]] bool testUnusedVariable = true;
[[maybe_unused]]
void TestUnusedFunction() {
}
[[nodiscard]] 标准属性
如果你的某个函数的返回值特别重要,希望使用者不要忽略,可以添加这个属性,再编译的时候如果函数使用者没有使用返回值将会有一个警告产生
example
[[nodiscard]] bool TestNodiscard() {
return true;
}
[[deprecated]] 标准属性
提示允许使用声明有此属性的名称或实体,但因为一些原因不鼓励使用,一般用在即将废弃的函数,但是还有老的用户使用到了这个函数
example
// 再vs中这个会不是警告而是错误.
[[deprecated("test deprecated")]] bool TestDeprecated() {
return true;
}
[[noretrun]] 标准属性
告知函数并没有返回值
example
[[noreturn]] void TestNoreturn() {
}
string_view
C++17
中特别新增的一个特别好用且重要的特性,string_view
相对于string
来说就是一个只读的string
,string_view
的赋值操作的空间成本和时间成本远远胜于string
,string_view
的赋值特别像一个指针的赋值,一般来说再一下情况下使用string_view
会更合适
exampel
// 常量string
const std::string = "hello world";
// string_view 更为合适
const string_view = "hello world";
// 函数参数
void Function1(const std::string& arg1) {
}
// string_view 更为合适
void Function1(string_view arg1) {
}
filesystem
在没有C++17
时一直就使用experiment/filesystem
,在C++17 filesystem
被正式纳入C++
标准库中, 由于大多数人对filesystem
都比较熟悉了,在这里只是简单的介绍一下
example
std::filesystem::path path("testpath");
if (std::filesystem::exists(path)) {
// 存在
} else {
// 不存在
}
any
any
是一个可用于任何类型单个值的类型安全容器,如果你之前有了解过boost
相信对这个any
类已经非常熟悉了
example
std::any Int = 69;
std::any Double = 69.123;
std::any String = std::string_view("Hello");
std::cout << Int.type().name() << std::endl;
std::cout << Double.type().name() << std::endl;
std::cout << Double.type().name() << std::endl;
std::vector<std::any> anys = { Int, Double, String };
std::cout << std::any_cast<int>(Int) << std::endl;
std::cout << std::any_cast<double>(Double) << std::endl;
std::cout << std::any_cast<std::string_view>(String) << std::endl;
// has_value: 是否有值
std::any a = 1;
if (a.has_value()) {
std::cout << a.type().name() << std::endl;// i
}
// reset:清空容器
a.reset();
if (a.has_value()) {
std::cout << "no value\n";// no value
}
optional
熟悉boost
的也应该非常熟悉optional
了,它最常用的地方是在你返回值是string
或者int
等出现错误之后非常隐式的表达的地方,使用std::optional
就可以帮你解决这种问题
example
[[nodiscard]]
std::optional<int> TestOptional() {
// 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
if (true) {
return 9999;
} else {
return std::nullopt;
}
}
[[nodiscard]]
std::optional<std::string> TestOptional2() {
// 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
if (true) {
return "helloworld";
} else {
return std::nullopt;
}
}
// optional
auto result = TestOptional();
if (result.has_value()) {
// 有值,代表成功
} else {
// result没有值代表失败
}
// 这个value_or表示当TestOptional的返回值为nullopt时使用or里面的值
auto ret = TestOptional2().value_or("");
variant
variant
用来表示一个类型安全的联合体,variant
的一个实例在任意时刻要么保有其一个可选类型之一的值,要么在错误情况下无值。
variant
不容许保有引用、数组,或类型 void
, 空variant
可以使用std::variant<std::monostate>
下面这个例子是一个实际接口设计时利用std::variant解决不同类型不同参数的接口
// variant
struct SystemProxyConfig {
bool isService;
};
struct CustomProxyConfig {
bool isFile;
std::string pathOrContent;
};
std::variant<SystemProxyConfig, CustomProxyConfig> config;
//config = CustomProxyConfig{ false, "http://192.168.21.161/spiderweb.pac" };
config = SystemProxyConfig{ false };
if (std::get_if<CustomProxyConfig>(&config)) {
// 类型成功
CustomProxyConfig customConfig = std::get<CustomProxyConfig>(config);
} else {
// 类型失败
SystemProxyConfig systemProxyConfig = std::get<SystemProxyConfig>(config);
int i = 0;
}
execution
execution
为C++STL
算法库提供了一种算法的执行策略设置,目前支持的策略:
sequenced_policy
(顺序执行策略)parallel_policy
(并行执行策略)parallel_unsequenced_policy
(并行及无序执行策略)unsequenced_policy
(无序执行策略)
example
std::vector<int> testExecution{ 1, 2, 3, 4, 5,8, 19, 20 ,30,40,50,0,102,40,10,30,20,1000,32,31,34,45};
auto it1 = std::find(std::execution::seq, testExecution.begin(), testExecution.end(), 5);
auto it2 = std::find(std::execution::par, testExecution.begin(), testExecution.end(), 5);
auto it3 = std::find(std::execution::par_unseq, testExecution.begin(), testExecution.end(), 5);