C++ 随心记5 C++17 较好用的新特性 (一)令代码简洁的特性

        这部分大体包括语法糖(封装优化好的库函数或模块),以及一些令代码更简洁、可读性更强的函数与库。

1. 结构化绑定

        C++ 17以前,使用for循环遍历map / unordered_map 时候需要声明一个中间变量(通常无意义),通过中间变量和first、second关键字访问key和value。

        C++ 17引入结构化绑定,其将array、tuple或struct的成员绑定到一组变量*

// 1.构建一个描述人的结构体
struct Person {
  int age = 0;
  std::string name;
};

// 2. 获得一个结构体对象,姓名:”ABC“,年龄:99
Person getPerson() {
  return Person{99, "ABC"};
};

// 3. 结构化绑定
auto [age, name] = getStruct();
// C++ 17 以前遍历 map,声明无意义引用elem
for(const auto& elem : map){
  const auto& key = elem.first;
  const auto& value = elem.second;
  std::cout << elem.first << ": "  << elem.second << std::endl;
}

// C++ 17 
for(const auto& [key, value]: map){
  std::cout << key << ": " << val << std::endl;
}

        *: 严格来说,结构化绑定的结果并不是变量,即 key 和 value 并非变量,C++标准称之为“别名”,因此它们不能被lambda表达式捕获,但是 gcc 并没有遵循 C++ 标准,所以下面代码在gcc下可以编译,clang则编译不过。

for(const auto& [key, value]: map){
    [&key, &value]{
        std::cout << key << ": " << value << std::endl;
    }();
}

        若使用Clang作为编译前端,可以在lambda表达式捕获时显式引入一个引用变量通过编译。

for(const auto& [key, value]: map){
    [&key = key, &value = value]{
        std::cout << key << ": " << value << std::endl;
    }();
}

        但是!该限制在C++ 20 中已经被删除,所以在C++ 20 的标准中gcc和Clang都可以捕获结构化绑定的对象了。

2. std::tuple的隐式推导

std::tuple 的推导指引 - cppreference.com

        在C++ 17以前,构造 std::pair / std::tuple 时必须指定数据类型或使用std::make_pair/std::make_tuple函数,C++ 17为std::pair/std::tuple新增了推导规则,可以不再显示指定类型。

// C++ 17 以前
std::pair p1{99, "ABC"s};
auto p1 = std::make_pair(99, "ABC"s);

// C++ 17 pair的具体类型隐式声明
std::pair p3{99, "ABC"s};

3. if constexpr

        if constexpr 语句是编译期的 if 判断语句,它可以在编译阶段,根据模板参数的值编译相应的段落,因此可以在“范型编程中使用” ;若是将if判断放到运行时,可能会造成性能损耗。在C++17 编译期的条件判断需要通过SFINAE 机制 或 模版重载 实现;

// C++ 17 使用变长参数模板 和 if constexpr 实现数字求和
template 
auto sum()
{
    if constexpr (0 == sizeof...(Ns))
        return N;
    else
        return N + sum();
}

// 调用
sum<1, 2, 3>(); // returns 6

         在没有if constexpr 之前,普通的 if - else 是在执行期进行条件判断,因此无法在泛型编程中无法使用。在这个求和的例子中,无法使用 if - else 判断当前变长参数的长度,因此需要额外写一个函数模板处理单模版参数的情况。

// 一个模板参数时的函数模板?拗口
template
int sum()
{
    return N;
}

// 模板参数 > 2 个时调用此模板
template 
int sum()
{
    return N1 + sum();
}

// 调用
sum<1, 2, 3>(); // returns 6

4. if / switch 语句内 支持初始化 

        C++ 17 支持在 if 语句内的条件判断之前增加一个初始化语句,将仅用于 if 语句内部的变量声明在此,提升代码可读性。

        可以包含

if (初始化语句; 条件) 语句 else 语句
switch (初始化语句; 条件) 语句
std::map m;
std::mutex mx;
extern bool shared_flag; // guarded by mx
 
int demo()
{
    if (auto it = m.find(10); it != m.end()) { return it->second.size(); }
    if (char buf[10]; std::fgets(buf, 10, stdin)) { m[0] += buf; }
    if (std::lock_guard lock(mx); shared_flag) { unsafe_ping(); shared_flag = false; }
    if (int s; int count = ReadBytesWithSignal(&s)) { publish(count); raise(s); }
    if (const auto keywords = {"if", "for", "while"};
        std::ranges::any_of(keywords, [&tok](const char* kw) { return tok == kw; }))
    {
        std::cerr << "Token must not be a keyword\n";
    }
}

你可能感兴趣的:(C++,学习,+,各类细节研讨,学习,开发语言)