C++ 11 的一些新特性

原始字面量

R("string...")

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int main() {
std::string str = R"(../../text.cc

上面换了个行)";
std::cout << str << std::endl;
}

[Running] cd "/root/code-server/c11-new/" && g++ 字符串字面量.cc -o 字符串字面量 && "/root/code-server/c11-new/"字符串字面量
../../text.cc

上面换了个行

[Done] exited with code=0 in 0.269 seconds

nullptr

这个没什么好说的,替换 NULL 使用即可,这玩意不等于 0

auto & decltype 自动推导

auto 只能推导初始化了的变量

decltype 根据表达式进行推导: decltype (表达式)

decltype 的特殊情况:

  • 如果表达式为函数调用,则推导的类型和函数返回值相同
  • 如果表达式为左值或者被 () 包围,推导出来的是表达式类型的引用

骚操作:

  • 返回类型后置
1
2
3
4
5
6
7
/**
* @brief 阻塞等待 set_value
* @return ret_type
*/
auto get_return() -> decltype(p_.get_future().get()) {
return p_.get_future().get();
}
  • 使用 decltype 的推导规则进行类型推导,auto 有些时候会推导错误
1
2
3
4
5
6
7
/**
* @brief 阻塞等待 set_value
* @return ret_type
*/
decltype(auto) get_return() {
return p_.get_future().get();
}

final & override

没啥好说的

函数模板默认参数

如题:

1
2
3
4
template<typename ret_type=int>
decltype(auto) get_return(ret_type _ret) {
return _ret
}

要注意的是,函数模板默认参数没有函数默认参数的默认参数都必须在右边的限制,想放哪就放哪

委托构造函数

允许构造函数调用其他构造函数,建议在初始化列表中使用

1
2
3
4
5
public:
explicit TestTask(std::string _name) : name_(std::move(_name)) {};
TestTask(std::string _name, const std::string& _text) : TestTask(std::move(_name)) {
std::cout << _text << std::endl;
}

继承构造函数

派生类可以直接使用基类的构造函数: using qualifier::name;

1
2
3
4
5
6
7
8
9
10
11
12
13
class TestTask : public XTask<std::string> {
public:
std::string name_;

public:
TestTask() = default;
explicit TestTask(std::string _name) : name_(std::move(_name)){};
}

class Task : public TestTask {
public:
using TestTask::TestTask;
};

也可以通过这种方式来使用基类的隐藏的同名函数

std::initializer_list

一个轻量的类模板,通过这个模板可以实现任意长度参数的传递

传参的时候可以通过实例化 std::initializer_list 或者使用初始化列表 { } 来进行传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

template<typename T>
void set_return(std::initializer_list<T> _list) {
for (const auto &i : _list){
std::cout << i;
}
std::cout << std::endl;
}

int main() {
std::initializer_list<int> list{1, 2, 3, 4, 5};
set_return(list);
set_return({1, 2, 3, 4, 5});

return 0;
}

同时也可以在构造函数中使用 std::initializer_list 来传递多个实参

for

1
2
3
for (const auto &i : _list){
std::cout << i;
}

其实就是使用迭代器来遍历容器

1
2
3
for (auto it = _list.begin(); it != _list.end(); ++it){
std::cout << i;
}

包装器和绑定器

右值引用

  • 左值:放在内存、有明确存储地址(可以取地址)的数据

  • 右值:可以提供数据值的数据(不可以取地址)

人话:能对表达式取地址的是左值,否则为右值,即:有名字的变量或对象都是左值,右值都是匿名的

右值又分两种:

  • 将亡值 (xvalue, expiring value): 非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和 lambda 表达式等
  • 纯右值 (prvalue, PureRvalue): 与右值引用相关的表达式,比如,T&& 类型函数的返回值、 std::move 的返回值等

右值引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Test {
Test() = default;
}

Test GetTest() {
return Test();
}

int main() {
int &&value = 666;
Test &t = GetTest();
Test &&t = GetTest();
const Test &t = GetTest();
return 0;
}

666 是纯右值,int &&value = 666; 没毛病

Test &t = GetTest(); 将一个右值赋值给左值引用,出大问题;Test &&t = GetTest(); 就没毛病

const Test &t = GetTest(); 有点特殊:常量左值引用是一个万能引用类型,可以接受左值、右值、常量左值、常量右值

为什么使用右值引用?

充分利用临时变量,减少不必要的拷贝