c++14特性

C++14特性

二进制字面值

二进制字面量提供了一种表示二进制数的便捷方式。可以用 ' 分隔数字。

0b110
0b1111'1111

泛型 lambda 表达式

允许在参数列表中使用 auto 类型说明符,从而实现多态 lambda

auto identity = [](auto x) { return x; };
int three = identity(3); // == 3
std::string foo = identity("foo"); // == "foo"

Lambda 捕获初始化器

这个特性允许你在捕获变量时同时初始化它们,可以为捕获的变量指定初始值,而不必在外部定义或修改它们

[!note]
初始化表达式在 lambda 创建时(而不是调用时)进行求值

#include <iostream>

int main() {
    int x = 10;
    int y = 20;

    // 使用捕获初始化器来指定捕获变量的初始值
    auto lambda = [ x, y = y + 5]() mutable {
        std::cout << "Captured x: " << x << ", y: " << ++y << std::endl;
    };

    // 打印捕获的变量
    lambda(); //Captured x: 10, y: 26

    x = 30;
    y = 40;
    lambda(); //Captured x: 10, y: 27

    auto labmda_ref = [&a = x, y = y + 1]() mutable {
        std::cout << "Captured a: " << a << ", y: " << y << std::endl;
    };

    labmda_ref(); //Captured a: 30, y: 41

    return 0;
}
  • [ x, y = y + 5]: 捕获了变量x的值,并在捕获时将y初始化为y+1的结果
  • [&a = x, y = y + 1]: 通过引用捕获了x,且可以设置与被引用变量不同的名称
  • 在捕获初始化器中创建的变量是属于Lambda内部的局部变量,不影响外部同名的变量

当前可以通过移动捕获的方式捕获移动类型

#include <memory>
#include <iostream>
int main() {
    auto p = std::make_unique<int>(1);

    auto lambda = [p = std::move(p)]() mutable  {
        *p = 5;
        return std::move(p);
    };

    p = lambda();

    std::cout << *p << std::endl;
    return 0;
}

注意

  • p是通过移动捕获传递给Lambda的,移动之后,原来的p变为空指针(nullptr
  • lambda捕获的变量默认是不可更改的,使用mutable以支持更改

返回类型推导

使用 auto 返回类型,编译器将尝试为您推导类型。对于 lambda 表达式,可以使用 auto 推导其返回类型

//return type as `int`
auto f(int i){
    return i;
}
template <typename T>
auto& f(T& t) {
  return t;
}

// Returns a reference to a deduced type.
auto g = [](auto& x) -> auto& { return f(x); };
int y = 123;
int& z = g(y); // reference to `y`

decltype(auto)

decltype(auto)结合了 decltypeauto 的特性,允许编译器推导出一个更精确的类型

  • auto 会推断出返回值的类型,但它会忽略引用和常量性
  • decltype 会根据表达式的实际类型推导,包括引用和常量性

使用场景

  1. 函数返回类型推导decltype(auto) 常用于自动推导函数返回类型,特别是当返回值可能是一个引用时。
  2. 函数内部变量:也可以用于局部变量的类型推导。
#include <iostream>
#include <vector>

int& getElement(std::vector<int>& vec, size_t index) {
    return vec[index];
}

decltype(auto) returnElement(std::vector<int>& vec, size_t index) {
    return getElement(vec, index);  // 使用decltype(auto),可以确保返回引用类型
}

int main() {
    std::vector<int> vec = {10, 20, 30};

    decltype(auto) ref = returnElement(vec, 1);  // ref 是 int& 类型
    std::cout << ref << std::endl;  // 输出:20

    ref = 50;  // 修改 vec[1] 的值
    std::cout << vec[1] << std::endl;  // 输出:50
}

变量模板

C++14允许变量被模板化

template <typename T>
constexpr T pi = T(3.1415926535897932385);  // 一个模板变量
  • template <typename T>:定义一个类型模板。
  • constexpr T pi = T(3.1415926535897932385);:这是模板变量的定义,pi 是常量变量,其类型依赖于模板参数 T,并且可以在不同类型下进行实例化。

示例

#include <iostream>

// 定义一个模板变量 pi
template <typename T>
constexpr T pi = T(3.1415926535897932385);

int main() {
    std::cout << "pi<double>: " << pi<double> << std::endl;  // 适用于 double 类型
    std::cout << "pi<float>: " << pi<float> << std::endl;    // 适用于 float 类型
    std::cout << "pi<int>: " << pi<int> << std::endl;        // 适用于 int 类型(将 3.14159 截断为 3)
}

常量模板与不同类型的类型推导:

#include <iostream>

template <typename T>
constexpr T max_value = T(100);  // 默认常量值为 100

int main() {
    std::cout << "max_value<int>: " << max_value<int> << std::endl;    // 100
    std::cout << "max_value<double>: " << max_value<double> << std::endl;  // 100.0
    std::cout << "max_value<char>: " << max_value<char> << std::endl;  // 'd'(对应的 ASCII 值)
}

结合 auto 和模板变量:

#include <iostream>

template <typename T>
constexpr T half = T(0.5);  // 定义模板变量 half

int main() {
    auto x = half<int>;     // 根据 int 类型推导,x 将是 0
    auto y = half<double>;  // 根据 double 类型推导,y 将是 0.5

    std::cout << "x: " << x << std::endl;  // 0
    std::cout << "y: " << y << std::endl;  // 0.5
}

根据类型选择不同的模板变量:

#include <iostream>

template <typename T>
constexpr T zero = T(0);

template <>
constexpr double zero<double> = 0.0;  // 针对 double 类型,零值为 0.0

int main() {
    std::cout << "zero<int>: " << zero<int> << std::endl;       // 0
    std::cout << "zero<double>: " << zero<double> << std::endl; // 0.0
}

编译时整数序列

类模板 std::integer_sequence 表示一个编译时整数序列

  • std::make_integer_sequence<T, N> - 创建一个类型为 T0, ..., N - 1 序列
  • std::index_sequence_for<T...> - 将模板参数包转换为整数序列

将数组转换为元组:

template<typename Array, std::size_t... I>
decltype(auto) a2t_impl(const Array& a, std::integer_sequence<std::size_t, I...>) {
  return std::make_tuple(a[I]...);
}

template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
decltype(auto) a2t(const std::array<T, N>& a) {
  return a2t_impl(a, Indices());
}

std::make_unique

推荐使用 std::make_unique 来创建 std::unique_ptr 实例

  • 避免内存泄漏make_unique 自动管理内存,避免忘记删除对象导致的内存泄漏。
  • 避免手动调用 newdelete:通过直接创建 unique_ptr,不用手动调用 new 或者 delete,减少了人为错误的风险。
  • 异常安全make_unique 在异常发生时能够自动清理已分配的内存,确保代码的异常安全性。
#include <iostream>
#include <memory>  // 包含 make_unique 和 unique_ptr

class MyClass {
public:
    MyClass(int val) : value(val) {
        std::cout << "MyClass constructed with value: " << value << std::endl;
    }
    
    void display() const {
        std::cout << "Value: " << value << std::endl;
    }

private:
    int value;
};

int main() {
    // 使用 make_unique 创建一个 unique_ptr
    std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(10);
    ptr->display();

    // unique_ptr 在作用域结束时自动释放内存
    return 0;
}

posted @ 2025-04-18 11:09  Midraos  阅读(94)  评论(0)    收藏  举报