C++14新特性

函数返回值类型推导

#include <iostream>
using namespace std;

// C++11不支持
template<typename T> 
auto func(T t) { 
	return t; 
}

int main() {
    cout << func(4) << endl;
    cout << func(3.4) << endl;
    return 0;
}
  • 如果有多个return语句,它们必须返回相同的类型,否则编译失败
auto func(bool flag) {
    if (flag) return 1;
    else return 2.3; // error
}
// inconsistent deduction for auto return type: ‘int’ and then ‘double’
  • 如果return语句返回初始化列表,返回值类型推导也会失败
auto func() {
    return {1, 2, 3}; // error returning initializer list
}
  • 如果函数是虚函数,不能使用返回值类型推导
struct A {
    // error: virtual function cannot have deduced return type
    virtual auto func() { return 1; } 
}
  • 返回类型推导可以用在前向声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义
auto f();               // declared, not yet defined
auto f() { return 42; } // defined, return type is int

int main() {
    cout << f() << endl;
}
  • 返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型
auto sum(int i) {
    if (i == 1)
        return i;              // return int
    else
        return sum(i - 1) + i; // ok
}

lambda参数auto

在C++14,lambda表达式参数可以直接是auto:

auto f = [] (auto a) { return a; };
cout << f(1) << endl;
cout << f(2.3f) << endl;

变量模板

template<class T>
constexpr T pi = T(3.1415926535897932385L);

int main() {
    cout << pi<int> << endl; // 3
    cout << pi<double> << endl; // 3.14159
    return 0;
}

别名模板

template<typename T, typename U>
struct A {
    T t;
    U u;
};

template<typename T>
using B = A<T, int>;

int main() {
    B<double> b;
    b.t = 10;
    b.u = 20;
    cout << b.t << endl;
    cout << b.u << endl;
    return 0;
}

constexpr的限制

C++11中constexpr函数必须必须把所有东西都放在一个单独的return语句中

// C++14 和 C++11均可
constexpr int factorial(int n) { 
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
// C++11中不可,C++14中可以
constexpr int factorial(int n) { 
    int ret = 0;
    for (int i = 0; i < n; ++i) {
        ret += i;
    }
    return ret;
}

[[deprecated]]标记

当程序中使用到了被其修饰的代码时,编译时被产生警告,用户提示开发者该标记修饰的内容将来可能会被丢弃,尽量不要使用

struct [[deprecated]] A { };

int main() {
    A a;
    return 0;
}

出现警告:

~/test$ g++ test.cc -std=c++14
test.cc: In function ‘int main():
test.cc:11:7: warning: ‘A’ is deprecated [-Wdeprecated-declarations]
     A a;
       ^
test.cc:6:23: note: declared here
 struct [[deprecated]] A {

二进制字面量与整形字面量分隔符

C++14引入了二进制字面量,也引入了分隔符,防止看起来眼花
可以使用 0b 或者 0B 前缀表示一个二进制字面量

int a = 0b0001'0011'1010;
double b = 3.14'1234'1234'1234;

std::make_unique

C++11中有std::make_shared,却没有std::make_unique,在C++14已经改善

struct A {};
std::unique_ptr<A> ptr = std::make_unique<A>();

std::shared_timed_mutex与std::shared_lock

C++14通过std::shared_timed_mutex和std::shared_lock来实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作不可以同时和读操作一起进行
std::lock_guard用于管理std::mutex
std::unique_lock与std::shared_lock管理std::shared_mutex
std::shared_timed_mutex
限时读写锁
特殊接口如下:

template< class Rep, class Period >
bool try_lock_shared_for( const std::chrono::duration<Rep,Period>& timeout_duration );

template< class Clock, class Duration >
bool try_lock_shared_until( const std::chrono::time_point<Clock,Duration>& timeout_time );
struct ThreadSafe {
    mutable std::shared_timed_mutex mutex_;
    int value_;

    ThreadSafe() {
        value_ = 0;
    }

    int get() const {
        std::shared_lock<std::shared_timed_mutex> loc(mutex_);
        return value_;
    }

    void increase() {
        std::unique_lock<std::shared_timed_mutex> lock(mutex_);
        value_ += 1;
    }
};

std::integer_sequence

作为模板元编程中的一员,主要用于生成编译时的整数序列。它本身并不存储任何数据,而是代表了一种类型,这种类型描述了一系列整数
这个模板类在 头文件中定义
特性概览:

  1. 类型安全性(Type-Safety)

std::integer_sequence 通过模板和类型参数化提供了编译时的类型检查,确保类型的一致性和安全性。

  1. 编译时确定(Compile-Time Evaluation)

序列中的整数在编译时就被确定,这意味着它们可以用于编译时的决策和计算,从而优化运行时性能。

  1. 类型表达(Type Representation)

它不直接存储数值,而是通过类型表达了一个整数序列。这一点体现了编程中“类型即文档(Types as Documentation)”的理念,使代码更加清晰和有表达力。

  1. 灵活性与扩展性(Flexibility and Extensibility)

作为模板,它可以与其他模板代码无缝集成,为复杂的模板元编程提供了强大的基础。

std::interger_sequence
template <class _Ty, _Ty... _Vals>
struct integer_sequence { // sequence of integer parameters
    static_assert(is_integral_v<_Ty>, "integer_sequence<T, I...> requires T to be an integral type.");
 
    using value_type = _Ty;
 
    _NODISCARD static constexpr size_t size() noexcept {
        return sizeof...(_Vals);
    }
};

通过static_assert和std::is_integral_v限制数据的类型为integral, 通过constexpr编译时求序列的大小。

std::index_sequence
template <size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;

它是一个类模板,表示数据类型为std::size_t的一个序列

std::make_index_sequence
template <class _Ty, _Ty _Size>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Ty, _Size>;

std::make_index_sequence是一个模板别名,它生成一个std::index_sequence类型的对象,该对象包含一系列递增的整数。这个工具在编译时生成一系列的索引,常常用于元编程和编译时计算。

template <size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;

std::size_t _size是一个模板参数,表示生成的序列的大小。std::make_integer_sequence是一个模板,它生成一个包含从0到N-1的整数序列的类型。

template <class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;

通常使用std::make_index_sequence来生成一个索引序列,然后使用这个序列来访问元组或数组的元素。这样,我们可以在编译时生成代码,而不需要在运行时进行循环。

示例 1:打印序列的值

#include <array>
#include <iostream>
#include <tuple>
#include <utility>
 
template <typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq) {
  std::cout << "The sequence of size " << int_seq.size() << ": ";
  ((std::cout << ints << ' '), ...);
  std::cout << '\n';
}
 
// 转换数组为 tuple
template <typename Array, std::size_t... I>
auto a2t_impl(const Array &a, std::index_sequence<I...>) {
  return std::make_tuple(a[I]...);
}
 
template <typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N> &a) {
  return a2t_impl(a, Indices{});
}
 
// 漂亮地打印 tuple
template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch, Tr> &os, const Tuple &t, std::index_sequence<Is...>) {
  ((os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}
 
template <class Ch, class Tr, class... Args>
auto &operator<<(std::basic_ostream<Ch, Tr> &os, const std::tuple<Args...> &t) {
  os << "(";
  print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
  return os << ")";
}
 
int main() {
  // The sequence of size 7: 9 2 5 1 9 1 6
  print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
  
  // The sequence of size 20: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  print_sequence(std::make_integer_sequence<int, 20>{});

  // The sequence of size 10: 0 1 2 3 4 5 6 7 8 9
  print_sequence(std::make_index_sequence<10>{});

  // The sequence of size 3: 0 1 2
  print_sequence(std::index_sequence_for<float, std::iostream, char>{});
 
  std::array<int, 4> array = {1, 2, 3, 4};
 
  // 转换 array 为 tuple
  auto tuple = a2t(array);
  static_assert(std::is_same<decltype(tuple), std::tuple<int, int, int, int>>::value, "");
 
  // 打印到 cout
  std::cout << tuple << '\n'; // (1, 2, 3, 4)
}

示例 2:使用序列作为函数参数

#include <utility>
#include <iostream>

template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>) {
    ((std::cout << Ints << ' '), ...);
}

int main() {
    // 创建一个包含 0, 1, 2, 3, 4 的序列
    auto seq = std::make_integer_sequence<int, 5>{};

    // 打印序列中的所有数字
    print_sequence(seq);
}

在编译时对整数序列进行复杂的操作

示例 3:使用序列进行函数参数解包

#include <utility>
#include <iostream>

// 打印函数,用于输出一个参数
template<typename T>
void print(T t) {
    std::cout << t << ' ';
}

// 递归终止函数
void print() {}

// 展开并打印参数包中的每个参数
template<typename T, typename... Args>
void print(T t, Args... args) {
    print(t);
    print(args...);
}

// 主函数,接受任意数量的参数,并打印它们
template<typename... Args, std::size_t... Is>
void printArgs(std::index_sequence<Is...>, Args... args) {
    print(args...);
    std::cout << std::endl;
}

int main() {
    printArgs(std::index_sequence_for<int, double, std::string>(), 42, 3.14, "Hello, World!");
    return 0;
}

利用 std::index_sequence_for 生成了一个与参数包 args 等长的序列。之后,通过参数包展开和序列,我们能够逐一处理每个参数

示例 4:编译时排序
可以在编译时对常量数据进行排序,从而提高运行时性能。

#include <utility>
#include <iostream>
#include <type_traits>

template<int... Ints>
using IntSequence = std::integer_sequence<int, Ints...>;

template<typename Seq, int N>
struct PushFront;

template<int... Ints, int N>
struct PushFront<IntSequence<Ints...>, N> {
    using type = IntSequence<N, Ints...>;
};

template<typename Seq1, typename Seq2>
struct Concat;

template<int... Ints1, int... Ints2>
struct Concat<IntSequence<Ints1...>, IntSequence<Ints2...>> {
    using type = IntSequence<Ints1..., Ints2...>;
};

template<typename Seq>
struct BubbleSort;

template<>
struct BubbleSort<IntSequence<>> {
    using type = IntSequence<>;
};

template<int First, int... Rest>
struct BubbleSort<IntSequence<First, Rest...>> {
private:
    using Next = typename BubbleSort<IntSequence<Rest...>>::type;
    static constexpr bool PushFrontFirst = (First <= Next::Head) || std::is_same<Next, IntSequence<>>::value;
public:
    using type = typename std::conditional<PushFrontFirst,
                                           typename PushFront<Next, First>::type,
                                           typename Concat<IntSequence<First>, typename PushFront<Next, Rest>::type>::type>::type;
};

int main() {
    using Unsorted = IntSequence<3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5>;
    using Sorted = BubbleSort<Unsorted>::type;
    
    // 输出排序结果
    std::cout << Sorted::size() << std::endl; // 输出排序后序列的大小
    return 0;
}

通过递归模板元编程和 std::integer_sequence 来在编译时对整数序列进行排序

std::integer_sequence和std::tuple的配合使用

template <std::size_t... Is, typename F, typename T>
auto map_filter_tuple(F f, T& t) {
    return std::make_tuple(f(std::get<Is>(t))...);
}

template <std::size_t... Is, typename F, typename T>
auto map_filter_tuple(std::index_sequence<Is...>, F f, T& t) {
    return std::make_tuple(f(std::get<Is>(t))...);
}

template <typename S, typename F, typename T>
auto map_filter_tuple(F&& f, T& t) {
    return map_filter_tuple(S{}, std::forward<F>(f), t);
}

#include <array>
#include <iostream>
#include <tuple>
#include <utility>
 
template <typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq) {
  std::cout << "The sequence of size " << int_seq.size() << ": ";
  ((std::cout << ints << ' '), ...);
  std::cout << '\n';
}
 
// 转换数组为 tuple
template <typename Array, std::size_t... I>
auto a2t_impl(const Array &a, std::index_sequence<I...>) {
  return std::make_tuple(a[I]...);
}
 
template <typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N> &a) {
  return a2t_impl(a, Indices{});
}
 
// 漂亮地打印 tuple
template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch, Tr> &os, const Tuple &t, std::index_sequence<Is...>) {
  ((os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}
 
template <class Ch, class Tr, class... Args>
auto &operator<<(std::basic_ostream<Ch, Tr> &os, const std::tuple<Args...> &t) {
  os << "(";
  print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
  return os << ")";
}
 
int main() {
  print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
  print_sequence(std::make_integer_sequence<int, 20>{});
  print_sequence(std::make_index_sequence<10>{});
  print_sequence(std::index_sequence_for<float, std::iostream, char>{});
 
  std::array<int, 4> array = {1, 2, 3, 4};
 
  // 转换 array 为 tuple
  auto tuple = a2t(array);
  static_assert(std::is_same<decltype(tuple), std::tuple<int, int, int, int>>::value, "");
 
  // 打印到 cout
  std::cout << tuple << '\n';
}
The sequence of size 7: 9 2 5 1 9 1 6
The sequence of size 20: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
The sequence of size 10: 0 1 2 3 4 5 6 7 8 9
The sequence of size 3: 0 1 2
(1, 2, 3, 4)

std::exchange

int main() {
    std::vector<int> v;
    std::exchange(v, {1,2,3,4});
    cout << v.size() << endl;
    for (int a : v) {
        cout << a << " ";
    }
    return 0;
}

exchange的实现

template<class T, class U = T>
constexpr T exchange(T& obj, U&& new_value) {
    T old_value = std::move(obj);
    obj = std::forward<U>(new_value);
    return old_value;
}

new_value的值给了obj,而没有对new_value赋值

std::quoted

用于给字符串添加双引号

int main() {
    string str = "hello world";
    cout << str << endl;
    cout << std::quoted(str) << endl;
    return 0;
}

编译&输出

hello world
"hello world"
  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值