logo头像
Snippet 博客主题

c++14新特性

1. 函数返回值类型推导

C++14新增函数返回值也可以使用auto自动推导了。

1
2
3
4
5
6
7
8
9
10
// 普通函数
auto func(int i) {
return i;
}

// 模板函数
template<typename T>
auto func(T i) {
return i;
}

但是注意下面特殊场景:

  1. 函数内如果有多个return语句,它们必须返回相同的类型,否则编译失败。

    1
    2
    3
    4
    5
    auto func(bool flag) {
    if (flag) return 1;
    else return 2.3; // error
    }
    // inconsistent deduction for auto return type: ‘int’ and then ‘double’
  2. 如果return语句返回初始化列表,返回值类型推导也会失败。

    1
    2
    3
    auto func() {
    return {1, 2, 3}; // error returning initializer list
    }
  3. 如果函数是虚函数,不能使用返回值类型推导。

    1
    2
    3
    4
    struct A {
    // error: virtual function cannot have deduced return type
    virtual auto func() { return 1; }
    }
  4. 返回类型推导可以用在前置声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义。

    1
    2
    3
    4
    5
    6
    auto f();               // declared, not yet defined
    auto f() { return 42; } // defined, return type is int

    int main() {
    cout << f() << endl;
    }
  5. 返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。

    1
    2
    3
    4
    5
    6
    auto sum(int i) {
    if (i == 1)
    return i; // return int
    else
    return sum(i - 1) + i; // ok
    }

2. lambda匿名函数参数使用auto

在C++11中,lambda表达式参数需要使用具体的类型声明。

1
auto f = [] (int a) { return a; }

在C++14中,对此进行优化,lambda表达式参数可以直接是auto。

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

3. 变量模板

C++14之前,模板有函数模板和类模板,C++14新增了变量模板。本质是用变量模板的默认值在实例化时进行强转,如果成功则成功,否则报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

template <class T>
constexpr T pi = T(3.1415926535897932385L); // 变量模板

int main()
{
cout << pi<int> << endl; // 3
cout << pi<double> << endl; // 3.14159
//cout << pi<string> << endl; // 无法从“long double”转换为“std::string”
return 0;
}

4. 别名模板

C++14支持别名模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.24;
b.u = 20;
cout << b.t << endl; // 10.24
cout << b.u << endl; // 20
return 0;
}

5. 减少constexpr的限制

  1. C++11中constexpr修饰的函数,函数体不可以使用局部变量,而C++14后可以。
1
2
3
4
5
6
7
constexpr int factorial(int n) { // C++11中不可,C++14中可以
int ret = 0;
for (int i = 0; i < n; ++i) {
ret += i;
}
return ret;
}
  1. C++11中constexpr修饰的函数只能有一个return语句,而C++14可以有多个。
1
2
3
4
constexpr int func(bool flag) { // C++11中不可,C++14中可以
if (flag) return 1;
else return 0;
}

6. 增加[[deprecated]]标记

C++14中增加了deprecated标记,修饰类、变量、函数等,编译时会产生警告,提示开发者该标记修饰的内容将来可能会被丢弃,尽量不要使用。

1
2
3
4
5
6
struct [[deprecated]] A { };

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

当编译时,会出现如下警告:

1
2
3
4
5
6
7
~/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 {

注:在vs2022上测试编译会报错,不是警告!

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

C++14引入了二进制字面量,也引入了分隔符。(防止看起来眼花)

1
2
3
4
5
6
7
#include <iostream>
int main()
{
int a = 0b0000'0000'0011;
double b = 3.14'1234'1234'1234;
std::cout << a << "\n" << b; // 3, 3.14123
}

8. std::make_unique

C++11中有std::make_shared,却没有std::make_unique,在C++14已经加入了。

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

9. std::shared_timed_mutex与std::shared_lock

C++14通过std::shared_timed_mutexstd::shared_lock来实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作不可以同时和读操作一起进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct ThreadSafe {
mutable std::shared_timed_mutex mutex_; // 互斥对象
int value_;

ThreadSafe() {
value_ = 0;
}

int read() const {
std::shared_lock<std::shared_timed_mutex> loc(mutex_); // 共享锁
return value_;
}

void write() {
std::unique_lock<std::shared_timed_mutex> lock(mutex_); // 独占锁
value_ += 1;
}
};

shared_timed_mutex这个比用shared_mutex多了超时时间设置。

10. std::integer_sequence

是一个模板类,可以在编译期生成整数序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
// 封装的提取整数序列的值接口
template <class T, T... Ints>
constexpr T get(std::integer_sequence<T, Ints...>, std::size_t i)
{
constexpr T arr[] = { Ints... };
return arr[i];
}

int main()
{
std::integer_sequence<int, 4, 6, 9> seq;
int index0 = get(seq, 0); // index0 = 4
int index2 = get(seq, 2); // index2 = 9
std::cout << index0 << "\n"
<< index2;
}

11. std::exchange

交换两个对象的值,T old_value = std::exchange(obj, new_value); 就是用new_value初始化obj,然后返回obj的旧值赋值给old_value

1
2
3
int a = 1;
int b = 2;
int old_a = std::exchange(a, b); // now a = 2, b = 1

注意它与std::swap的区别,这个内部实现用的右值移动语义,避免了临时对象和拷贝操作。常使用的场景包括:

  • 交换智能指针,实现原子指针交换
  • 交换互斥锁的状态
  • 替换容器中的元素
  • 交换线程状态

12. std::quoted

C++14添加的一个字符串转义的工具类,避免手动添加转义字符的麻烦。

1
2
3
4
5
std::string s1 = std::quoted("Hello \"World\"");
// s1 是 "Hello \"World\""

std::string s2 = std::quoted(R"(C:\Windows\System)");
// s2 是 "C:\\Windows\\System"