前项目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_viewC++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 {
- // 不存在
- }
复制代码 anyany是一个可用于任何类型单个值的类型安全容器,如果你之前有了解过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:ptional就可以帮你解决这种问题 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("");
复制代码 variantvariant用来表示一个类型安全的联合体,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;
- }
复制代码 executionexecution为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);
复制代码
|