C++17 新特性教程

C++17新特性

  • 前言
  • If Statements with Initializer
  • Constexpr if
  • inline 变量
  • 嵌套命名空间
  • 属性说明符
    • [[fallthrough]] 标准属性
    • [[maybe_unused]] 标准属性
    • [[nodiscard]] 标准属性
    • [[deprecated]] 标准属性
    • [[noretrun]] 标准属性
  • 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 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 
void TestConstexprIf(T value) {
    if constexpr (std::is_integral_v)
        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;

属性说明符

属性为各种由实现定义的语言扩展(例如 GNUIBM 的语言扩展 __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来说就是一个只读的stringstring_view的赋值操作的空间成本和时间成本远远胜于stringstring_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 anys = { Int, Double, String };
std::cout << std::any_cast(Int) << std::endl;
std::cout << std::any_cast(Double) << std::endl;
std::cout << std::any_cast(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 TestOptional() {
    // 之前我们可能需要使用return -1,代表错误,现在使用st::optional就不需要那种太过于隐式的表达
    if (true) {
        return 9999;
    } else {
        return std::nullopt;
    }
}

[[nodiscard]]
std::optional 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::variant解决不同类型不同参数的接口
// variant
struct SystemProxyConfig {
  bool isService;
};

struct CustomProxyConfig {
  bool isFile;
  std::string pathOrContent;
};
std::variant config;
//config = CustomProxyConfig{ false, "http://192.168.21.161/spiderweb.pac" };
config = SystemProxyConfig{ false };
if (std::get_if(&config)) {
  // 类型成功
  CustomProxyConfig customConfig = std::get(config);
} else {
  // 类型失败
  SystemProxyConfig systemProxyConfig = std::get(config);
  int i = 0;
}

execution

executionC++STL算法库提供了一种算法的执行策略设置,目前支持的策略:

  • sequenced_policy (顺序执行策略)
  • parallel_policy (并行执行策略)
  • parallel_unsequenced_policy (并行及无序执行策略)
  • unsequenced_policy (无序执行策略)

example

    std::vector 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);

参考链接

cppreference.com

fallthrough属性

你可能感兴趣的:(c++17,c++20,c++)