mirror of
https://github.com/changkun/modern-cpp-tutorial.git
synced 2025-12-17 04:34:40 +03:00
commit all code
This commit is contained in:
13
1/1.1.cpp
Normal file
13
1/1.1.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// 1.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
|
||||
#include "foo.h"
|
||||
|
||||
int main() {
|
||||
add(1, 2);
|
||||
return 0;
|
||||
}
|
||||
6
1/Makefile
Normal file
6
1/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
TARGET = 1.1
|
||||
all:
|
||||
gcc -c foo.c
|
||||
g++ 1.1.cpp foo.o -o $(TARGET)
|
||||
clean:
|
||||
rm -rf *.o $(TARGET)
|
||||
13
1/foo.c
Normal file
13
1/foo.c
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// foo.c
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
|
||||
|
||||
#include "foo.h"
|
||||
|
||||
int add(int x, int y) {
|
||||
return x+y;
|
||||
}
|
||||
17
1/foo.h
Normal file
17
1/foo.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// foo.h
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int add(int x, int y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
32
2/2.1.cpp
Normal file
32
2/2.1.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// 2.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// nullptr
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void foo(char *);
|
||||
void foo(int);
|
||||
|
||||
int main() {
|
||||
|
||||
if(NULL == (void *)0)
|
||||
std::cout << "NULL == 0" << std::endl; // 该行将输出
|
||||
else
|
||||
std::cout << "NULL != 0" << std::endl;
|
||||
|
||||
foo(0); // 调用 foo(int)
|
||||
//foo(NULL); // 该行不能通过编译
|
||||
foo(nullptr); // 调用 foo(char*)
|
||||
|
||||
return 0;
|
||||
}
|
||||
void foo(char *ch) {
|
||||
std::cout << "call foo(char*)" << std::endl;
|
||||
}
|
||||
void foo(int i) {
|
||||
std::cout << "call foo(int)" << std::endl;
|
||||
}
|
||||
34
2/2.2.cpp
Normal file
34
2/2.2.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// 2.2.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// constexpr
|
||||
|
||||
#include <iostream>
|
||||
#define LEN 10
|
||||
|
||||
constexpr int len_foo() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
constexpr int fibonacci(const int n) {
|
||||
return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char arr_1[10];
|
||||
|
||||
char arr_2[LEN];
|
||||
|
||||
const int len = 10;
|
||||
char arr_3[len];
|
||||
|
||||
char arr_5[len_foo()+5];
|
||||
|
||||
std::cout << fibonacci(10) << std::endl;
|
||||
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
2/2.3.cpp
Normal file
49
2/2.3.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// 2.3.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// auto/decltype/尾返回类型/返回类型推导
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// 传统 C++
|
||||
template <typename R, typename T, typename U>
|
||||
R add1(T x, U y) {
|
||||
return x+y;
|
||||
}
|
||||
|
||||
// 尾返回类型
|
||||
template <typename T, typename U>
|
||||
auto add2(T x, U y) -> decltype(x+y) {
|
||||
return x+y;
|
||||
}
|
||||
|
||||
// C++14 返回类型推导
|
||||
template <typename T, typename U>
|
||||
auto add3(T x, U y) {
|
||||
return x+y;
|
||||
}
|
||||
|
||||
int main() {
|
||||
auto i = 5;
|
||||
|
||||
int arr[10] = {0};
|
||||
auto auto_arr = arr; // 正确,对整个类型进行推导
|
||||
//auto auto_arr2[10] = arr; // 错误, 无法推导数组元素类型
|
||||
|
||||
auto x = 1;
|
||||
auto y = 2;
|
||||
decltype(x+y) z1;
|
||||
//auto z2; // 错误, 无法推导
|
||||
|
||||
|
||||
std::cout << add1<int, int, int>(1,1) << std::endl;
|
||||
std::cout << add1<int, int>(1,1) << std::endl;
|
||||
std::cout << add1<int, int>(1,1) << std::endl;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
30
2/2.4.cpp
Normal file
30
2/2.4.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// 2.4.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 区间迭代
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int main() {
|
||||
int array[] = {1,2,3,4,5};
|
||||
for(auto &x : array) {
|
||||
std::cout << x << std::endl;
|
||||
}
|
||||
|
||||
// 传统 C++ 写法
|
||||
std::vector<int> arr(5, 100);
|
||||
for(std::vector<int>::iterator i = arr.begin(); i != arr.end(); ++i) {
|
||||
std::cout << *i << std::endl;
|
||||
}
|
||||
|
||||
// C++11 写法
|
||||
// & 启用了引用, 如果没有则对 arr 中的元素只能读取不能修改
|
||||
for(auto &i : arr) {
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
34
2/2.5.cpp
Normal file
34
2/2.5.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// 2.5.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 初始化列表
|
||||
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
class Foo {
|
||||
private:
|
||||
int value;
|
||||
public:
|
||||
Foo(int) {}
|
||||
};
|
||||
|
||||
class Magic {
|
||||
public:
|
||||
Magic(std::initializer_list<int> list) {}
|
||||
};
|
||||
|
||||
void func(std::initializer_list<int> list) {
|
||||
return;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int arr[3] = {1,2,3}; // 列表初始化
|
||||
Foo foo(1); // 普通构造初始化
|
||||
|
||||
Magic magic = {1,2,3,4,5}; // 使用 initialize_list
|
||||
func({1,2,3});
|
||||
}
|
||||
76
2/2.6.cpp
Normal file
76
2/2.6.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// 2.6.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 模板增强
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
template class std::vector<bool>; // 强行实例化
|
||||
extern template class std::vector<double>; // 不在该编译文件中实例化模板
|
||||
|
||||
|
||||
template< typename T, typename U, int value>
|
||||
class SuckType {
|
||||
public:
|
||||
T a;
|
||||
U b;
|
||||
SuckType():a(value),b(value){}
|
||||
};
|
||||
// template< typename T>
|
||||
// typedef SuckType<std::vector<int>, T, 1> NewType; // 不合法
|
||||
|
||||
template <typename T>
|
||||
using NewType = SuckType<int, T, 1>; // 合法
|
||||
|
||||
// 默认模板类型
|
||||
template<typename T = int, typename U = int>
|
||||
auto add(T x, U y) -> decltype(x+y) {
|
||||
return x+y;
|
||||
}
|
||||
|
||||
// sizeof...
|
||||
template<typename... Args>
|
||||
void magic(Args... args) {
|
||||
std::cout << sizeof...(args) << std::endl;
|
||||
}
|
||||
|
||||
|
||||
// 1. 递归解参数包
|
||||
template<typename T>
|
||||
void printf1(T value) {
|
||||
std::cout << value << std::endl;
|
||||
}
|
||||
template<typename T, typename... Args>
|
||||
void printf1(T value, Args... args) {
|
||||
std::cout << value << std::endl;
|
||||
printf1(args...);
|
||||
}
|
||||
// 2.初始化列表展开解参数包
|
||||
template<typename T, typename... Args>
|
||||
auto printf2(T value, Args... args) {
|
||||
std::cout << value << std::endl;
|
||||
return std::initializer_list<T>{([&] {
|
||||
std::cout << args << std::endl;
|
||||
}(), value)...};
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
std::vector<std::vector<int>> wow; // 注意尖括号
|
||||
|
||||
NewType<int> t;
|
||||
|
||||
magic();
|
||||
magic(1);
|
||||
magic(1,"");
|
||||
|
||||
printf1(1, 2.3, "abc");
|
||||
printf2(1, 2.3, "abc");
|
||||
return 0;
|
||||
}
|
||||
65
2/2.7.cpp
Normal file
65
2/2.7.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// 2.7.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 面向对象增强
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
class Base {
|
||||
public:
|
||||
std::string str;
|
||||
int value;
|
||||
Base() = delete;
|
||||
Base(std::string s) {
|
||||
str = s;
|
||||
}
|
||||
|
||||
// 委托构造
|
||||
Base(std::string s, int v) : Base(s) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
// 终止重载
|
||||
virtual void foo() final {
|
||||
return;
|
||||
}
|
||||
virtual void foo(int v) {
|
||||
value = v;
|
||||
}
|
||||
};
|
||||
class Subclass final : public Base {
|
||||
public:
|
||||
double floating;
|
||||
Subclass() = delete;
|
||||
|
||||
// 继承构造
|
||||
Subclass(double f, int v, std::string s) : Base(s, v) {
|
||||
floating = f;
|
||||
}
|
||||
|
||||
// 显式重载
|
||||
virtual void foo(int v) override {
|
||||
std::cout << v << std::endl;
|
||||
value = v;
|
||||
}
|
||||
}; // 合法 final
|
||||
|
||||
// class Subclass2 : Subclass {
|
||||
// }; // 非法, Subclass 已 final
|
||||
// class Subclass3 : Base {
|
||||
// void foo(); // 非法, foo 已 final
|
||||
// }
|
||||
|
||||
int main() {
|
||||
// Subclass oops; // 非法, 默认构造已删除
|
||||
Subclass s(1.2, 3, "abc");
|
||||
|
||||
s.foo(1);
|
||||
|
||||
std::cout << s.floating << std::endl;
|
||||
std::cout << s.value << std::endl;
|
||||
std::cout << s.str << std::endl;
|
||||
}
|
||||
51
2/2.8.cpp
Normal file
51
2/2.8.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// 2.8.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 强类型枚举
|
||||
|
||||
#include <iostream>
|
||||
template<typename T>
|
||||
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
|
||||
{
|
||||
return stream << static_cast<typename std::underlying_type<T>::type>(e);
|
||||
}
|
||||
|
||||
// 如果两个都定义为 value1 和 value2,将引发重定义错误
|
||||
enum Left {
|
||||
left_value1 = 1,
|
||||
left_value2
|
||||
};
|
||||
enum Right {
|
||||
right_value1 = 1,
|
||||
right_value2
|
||||
};
|
||||
|
||||
enum class new_enum : unsigned int{
|
||||
value1,
|
||||
value2,
|
||||
value3 = 100,
|
||||
value4 = 100
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
if (Left::left_value1 == Right::right_value2) {
|
||||
std::cout << "Left::value1 == Right::value2" << std::endl;
|
||||
}
|
||||
|
||||
// 引发编译错误
|
||||
// if(new_enum::value1 == 1) {
|
||||
// std::cout << "true!" << std::endl;
|
||||
// }
|
||||
if (new_enum::value3 == new_enum::value4) {
|
||||
std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << new_enum::value3 << std::endl;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
52
3/3.1.cpp
Normal file
52
3/3.1.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// 3.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// lambda expression
|
||||
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
void learn_lambda_func_1() {
|
||||
int value_1 = 1;
|
||||
auto copy_value_1 = [value_1] {
|
||||
return value_1;
|
||||
};
|
||||
value_1 = 100;
|
||||
auto stored_value_1 = copy_value_1();
|
||||
// 这时, stored_value_1 == 1, 而 value_1 == 100.
|
||||
// 因为 copy_value_1 在创建时就保存了一份 value_1 的拷贝
|
||||
}
|
||||
|
||||
void learn_lambda_func_2() {
|
||||
int value_2 = 1;
|
||||
auto copy_value_2 = [&value_2] {
|
||||
return value_2;
|
||||
};
|
||||
value_2 = 100;
|
||||
auto stored_value_2 = copy_value_2();
|
||||
// 这时, stored_value_2 == 100, value_1 == 100.
|
||||
// 因为 copy_value_2 保存的是引用
|
||||
}
|
||||
|
||||
int main() {
|
||||
learn_lambda_func_1();
|
||||
learn_lambda_func_2();
|
||||
|
||||
auto important = std::make_unique<int>(1);
|
||||
auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
|
||||
return x+y+v1+(*v2);
|
||||
};
|
||||
std::cout << add(3,4) << std::endl;
|
||||
|
||||
// 泛型 lambda
|
||||
auto generic = [](auto x, auto y) {
|
||||
return x+y;
|
||||
};
|
||||
|
||||
generic(1, 2);
|
||||
generic(1.1, 2.2);
|
||||
return 0;
|
||||
}
|
||||
51
3/3.2.cpp
Normal file
51
3/3.2.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// 3.2.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// std::function std::bind
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
using foo = void(int); // 定义函数指针
|
||||
void functional(foo f) {
|
||||
f(1);
|
||||
}
|
||||
|
||||
int foo2(int para) {
|
||||
return para;
|
||||
}
|
||||
|
||||
int foo3(int a, int b, int c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
auto f = [](int value) {
|
||||
std::cout << value << std::endl;
|
||||
};
|
||||
functional(f); // 函数指针调用
|
||||
f(1); // lambda 表达式调用
|
||||
|
||||
// std::function 包装了一个返回值为 int, 参数为 int 的函数
|
||||
std::function<int(int)> func = foo2;
|
||||
|
||||
int important = 10;
|
||||
std::function<int(int)> func2 = [&](int value) -> int {
|
||||
return 1+value+important;
|
||||
};
|
||||
std::cout << func(10) << std::endl;
|
||||
std::cout << func2(10) << std::endl;
|
||||
|
||||
|
||||
// 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位
|
||||
auto bindFoo = std::bind(foo3, std::placeholders::_1, 1,2);
|
||||
// 这时调用 bindFoo 时,只需要提供第一个参数即可
|
||||
bindFoo(1);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
38
3/3.3.cpp
Normal file
38
3/3.3.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// 3.3.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 右值引用 rvalue reference
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
void reference(std::string& str) {
|
||||
std::cout << "左值" << std::endl;
|
||||
}
|
||||
void reference(std::string&& str) {
|
||||
std::cout << "右值" << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string lv1 = "string,"; // lv1 是一个左值
|
||||
// std::string&& r1 = s1; // 非法, 右值引用不能引用左值
|
||||
std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值
|
||||
std::cout << rv1 << std::endl; // string,
|
||||
|
||||
const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期
|
||||
// lv2 += "Test"; // 非法, 引用的右值无法被修改
|
||||
std::cout << lv2 << std::endl; // string,string
|
||||
|
||||
std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期
|
||||
rv2 += "string"; // 合法, 非常量引用能够修改临时变量
|
||||
std::cout << rv2 << std::endl; // string,string,string,
|
||||
|
||||
reference(rv2); // 输出左值
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
3/3.4.cpp
Normal file
31
3/3.4.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// 3.4.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 移动语义
|
||||
|
||||
#include <iostream>
|
||||
class A {
|
||||
public:
|
||||
int *pointer;
|
||||
A():pointer(new int(1)) { std::cout << "构造" << pointer << std::endl; }
|
||||
A(A& a):pointer(new int(*a.pointer)) { std::cout << "拷贝" << pointer << std::endl; } // 无意义的对象拷贝
|
||||
A(A&& a):pointer(a.pointer) { a.pointer = nullptr;std::cout << "移动" << pointer << std::endl; }
|
||||
~A(){ std::cout << "析构" << pointer << std::endl; delete pointer; }
|
||||
};
|
||||
// 防止编译器优化
|
||||
A return_rvalue(bool test) {
|
||||
A a,b;
|
||||
if(test) return a;
|
||||
else return b;
|
||||
}
|
||||
int main() {
|
||||
A obj = return_rvalue(false);
|
||||
std::cout << "obj:" << std::endl;
|
||||
std::cout << obj.pointer << std::endl;
|
||||
std::cout << *obj.pointer << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
32
3/3.5.cpp
Normal file
32
3/3.5.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// 3.5.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 移动语义
|
||||
|
||||
#include <iostream> // std::cout
|
||||
#include <utility> // std::move
|
||||
#include <vector> // std::vector
|
||||
#include <string> // std::string
|
||||
|
||||
int main() {
|
||||
|
||||
std::string str = "Hello world.";
|
||||
std::vector<std::string> v;
|
||||
|
||||
// 将使用 push_back(const T&), 即产生拷贝行为
|
||||
v.push_back(str);
|
||||
// 将输出 "str: Hello world."
|
||||
std::cout << "str: " << str << std::endl;
|
||||
|
||||
// 将使用 push_back(const T&&), 不会出现拷贝行为
|
||||
// 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销
|
||||
// 这步操作后, str 中的值会变为空
|
||||
v.push_back(std::move(str));
|
||||
// 将输出 "str: "
|
||||
std::cout << "str: " << str << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
3/3.6.cpp
Normal file
36
3/3.6.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// 3.6.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 完美转发
|
||||
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
void reference(int& v) {
|
||||
std::cout << "左值引用" << std::endl;
|
||||
}
|
||||
void reference(int&& v) {
|
||||
std::cout << "右值引用" << std::endl;
|
||||
}
|
||||
template <typename T>
|
||||
void pass(T&& v) {
|
||||
std::cout << "普通传参:";
|
||||
reference(v);
|
||||
std::cout << "std::move 传参:";
|
||||
reference(std::move(v));
|
||||
std::cout << "std::forward 传参:";
|
||||
reference(std::forward<T>(v));
|
||||
|
||||
}
|
||||
int main() {
|
||||
std::cout << "传递右值:" << std::endl;
|
||||
pass(1);
|
||||
|
||||
std::cout << "传递左值:" << std::endl;
|
||||
int v = 1;
|
||||
pass(v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
35
4/4.1.cpp
Normal file
35
4/4.1.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 4.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// std::array
|
||||
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
|
||||
void foo(int *p, int len) {
|
||||
for (int i = 0; i != len; ++i) {
|
||||
std::cout << p[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::array<int, 4> arr= {1,4,3,2};
|
||||
|
||||
//int len = 4;
|
||||
//std::array<int, len> arr = {1,2,3,4}; // 非法, 数组大小参数必须是常量表达式
|
||||
|
||||
// C 风格接口传参
|
||||
// foo(arr, arr.size()); // 非法, 无法隐式转换
|
||||
foo(&arr[0], arr.size());
|
||||
foo(arr.data(), arr.size());
|
||||
|
||||
// 更多接口使用
|
||||
std::sort(arr.begin(), arr.end());
|
||||
for(auto &i : arr)
|
||||
std::cout << i << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
4/4.2.cpp
Normal file
36
4/4.2.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// 4.2.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 无序容器
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
int main() {
|
||||
// 两组结构按同样的顺序初始化
|
||||
std::unordered_map<int, std::string> u = {
|
||||
{1, "1"},
|
||||
{3, "3"},
|
||||
{2, "2"}
|
||||
};
|
||||
std::map<int, std::string> v = {
|
||||
{1, "1"},
|
||||
{3, "3"},
|
||||
{2, "2"}
|
||||
};
|
||||
|
||||
// 分别对两组结构进行遍历
|
||||
std::cout << "std::unordered_map" << std::endl;
|
||||
for( const auto & n : u)
|
||||
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "std::map" << std::endl;
|
||||
for( const auto & n : v)
|
||||
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
|
||||
}
|
||||
77
4/4.3.cpp
Normal file
77
4/4.3.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// 4.3.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// std::tuple 及其操作
|
||||
|
||||
|
||||
#include <tuple>
|
||||
#include <iostream>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
auto get_student(int id)
|
||||
{
|
||||
if (id == 0)
|
||||
return std::make_tuple(3.8, 'A', "张三");
|
||||
if (id == 1)
|
||||
return std::make_tuple(2.9, 'C', "李四");
|
||||
if (id == 2)
|
||||
return std::make_tuple(1.7, 'D', "王五");
|
||||
// 返回类型被推断为 std::tuple<double, char, std::string>
|
||||
return std::make_tuple(0.0, 'D', "null");
|
||||
}
|
||||
|
||||
template <size_t n, typename... T>
|
||||
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
|
||||
if (i == n)
|
||||
return std::get<n>(tpl);
|
||||
else if (n == sizeof...(T) - 1)
|
||||
throw std::out_of_range("越界.");
|
||||
else
|
||||
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
|
||||
}
|
||||
template <typename... T>
|
||||
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
|
||||
return _tuple_index<0>(i, tpl);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto tuple_len(T &tpl) {
|
||||
return std::tuple_size<T>::value;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto student = get_student(0);
|
||||
std::cout << "ID: 0, "
|
||||
<< "GPA: " << std::get<0>(student) << ", "
|
||||
<< "成绩: " << std::get<1>(student) << ", "
|
||||
<< "姓名: " << std::get<2>(student) << '\n';
|
||||
|
||||
double gpa;
|
||||
char grade;
|
||||
std::string name;
|
||||
|
||||
// 元组进行拆包
|
||||
std::tie(gpa, grade, name) = get_student(1);
|
||||
std::cout << "ID: 1, "
|
||||
<< "GPA: " << gpa << ", "
|
||||
<< "成绩: " << grade << ", "
|
||||
<< "姓名: " << name << '\n';
|
||||
|
||||
|
||||
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
|
||||
std::cout << std::get<std::string>(t) << std::endl;
|
||||
// std::cout << std::get<double>(t) << std::endl; // 非法, 引发编译期错误
|
||||
std::cout << std::get<3>(t) << std::endl;
|
||||
|
||||
// 拼接元组
|
||||
auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
|
||||
|
||||
// 迭代
|
||||
for(int i = 0; i != tuple_len(new_tuple); ++i)
|
||||
// 运行期索引
|
||||
std::cout << tuple_index(i, new_tuple) << std::endl;
|
||||
}
|
||||
66
5/5.1.cpp
Normal file
66
5/5.1.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
void foo(std::shared_ptr<int> i)
|
||||
{
|
||||
(*i)++;
|
||||
}
|
||||
|
||||
|
||||
//struct Base {
|
||||
// Base() { std::cout << " Base::Base()\n"; }
|
||||
// ~Base() { std::cout << " Base::~Base()\n"; }
|
||||
//};
|
||||
//
|
||||
//struct Subclass: public Base {
|
||||
// Subclass() { std::cout << " Subclass::Subclass()\n"; }
|
||||
// ~Subclass() { std::cout << " Subclass::~Subclass()\n"; }
|
||||
//};
|
||||
//
|
||||
//void thr(std::shared_ptr<Base> p)
|
||||
//{
|
||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// std::shared_ptr<Base> lp = p; // 即使引用计数增加也是线程安全的
|
||||
// {
|
||||
// static std::mutex io_mutex;
|
||||
// std::lock_guard<std::mutex> lk(io_mutex);
|
||||
// std::cout << "local pointer in a thread:\n"
|
||||
// << " lp.get() = " << lp.get()
|
||||
// << ", lp.use_count() = " << lp.use_count() << '\n';
|
||||
// }
|
||||
//}
|
||||
|
||||
int main()
|
||||
{
|
||||
// auto pointer = new int(10); // 非法, 不允许直接赋值
|
||||
// 构造了一个 std::shared_ptr
|
||||
auto pointer = std::make_shared<int>(10);
|
||||
auto pointer2 = pointer; // 引用计数+1
|
||||
auto pointer3 = pointer; // 引用计数+1
|
||||
|
||||
|
||||
foo(pointer);
|
||||
std::cout << *pointer << std::endl; // 11
|
||||
int *p = pointer.get(); // 这样不会增加引用计数
|
||||
|
||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
|
||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
|
||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
|
||||
|
||||
pointer2.reset();
|
||||
std::cout << "reset pointer2:" << std::endl;
|
||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
|
||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
|
||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
|
||||
|
||||
pointer3.reset();
|
||||
std::cout << "reset pointer3:" << std::endl;
|
||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
|
||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
|
||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
|
||||
// std::cout << *pointer << std::endl; // 引用计数为0时, 非法访问
|
||||
|
||||
|
||||
// 离开作用域前,pointer 会被析构,引用计数减为0, 从而释放内存
|
||||
return 0;
|
||||
}
|
||||
41
5/5.2.cpp
Normal file
41
5/5.2.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
struct Foo {
|
||||
Foo() { std::cout << "Foo::Foo" << std::endl; }
|
||||
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
|
||||
void foo() { std::cout << "Foo::foo" << std::endl; }
|
||||
};
|
||||
|
||||
void f(const Foo &) {
|
||||
std::cout << "f(const Foo&)" << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
|
||||
|
||||
// p1 不空, 输出
|
||||
if (p1) p1->foo();
|
||||
{
|
||||
std::unique_ptr<Foo> p2(std::move(p1));
|
||||
|
||||
// p2 不空, 输出
|
||||
f(*p2);
|
||||
|
||||
// p2 不空, 输出
|
||||
if(p2) p2->foo();
|
||||
|
||||
// p1 为空, 无输出
|
||||
if(p1) p1->foo();
|
||||
|
||||
p1 = std::move(p2);
|
||||
|
||||
// p2 为空, 无输出
|
||||
if(p2) p2->foo();
|
||||
std::cout << "p2 被销毁" << std::endl;
|
||||
}
|
||||
// p1 不空, 输出
|
||||
if (p1) p1->foo();
|
||||
|
||||
// Foo 的实例会在离开作用域时被销毁
|
||||
}
|
||||
28
5/5.3.cpp
Normal file
28
5/5.3.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
class A;
|
||||
class B;
|
||||
|
||||
class A {
|
||||
public:
|
||||
std::shared_ptr<B> pointer;
|
||||
~A() {
|
||||
std::cout << "A 被销毁" << std::endl;
|
||||
}
|
||||
};
|
||||
class B {
|
||||
public:
|
||||
std::shared_ptr<A> pointer;
|
||||
~B() {
|
||||
std::cout << "B 被销毁" << std::endl;
|
||||
}
|
||||
};
|
||||
int main() {
|
||||
std::shared_ptr<A> a = std::make_shared<A>();
|
||||
std::shared_ptr<B> b = std::make_shared<B>();
|
||||
a->pointer = b;
|
||||
b->pointer = a;
|
||||
|
||||
return 0;
|
||||
}
|
||||
35
6/6.1.cpp
Normal file
35
6/6.1.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 6.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 正则表达式库
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
int main() {
|
||||
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
||||
// 在 C++ 中 `\` 会被作为字符串内的转义符,为使 `\.` 作为正则表达式传递进去生效,需要对 `\` 进行二次转义,从而有 `\\.`
|
||||
std::regex txt_regex("[a-z]+\\.txt");
|
||||
for (const auto &fname: fnames)
|
||||
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
||||
|
||||
std::regex base_regex("([a-z]+)\\.txt");
|
||||
std::smatch base_match;
|
||||
for(const auto &fname: fnames) {
|
||||
if (std::regex_match(fname, base_match, base_regex)) {
|
||||
// sub_match 的第一个元素匹配整个字符串
|
||||
// sub_match 的第二个元素匹配了第一个括号表达式
|
||||
if (base_match.size() == 2) {
|
||||
std::string base = base_match[1].str();
|
||||
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
|
||||
std::cout << fname << " sub-match[1]: " << base << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
7/7.1.cpp
Normal file
36
7/7.1.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// 7.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 线程支持库
|
||||
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
void foo() {
|
||||
std::cout << "hello world" << std::endl;
|
||||
}
|
||||
|
||||
void foo2() {
|
||||
// 将一个返回值为7的 lambda 表达式封装到 task 中
|
||||
// std::packaged_task 的模板参数为要封装函数的类型
|
||||
std::packaged_task<int()> task([](){return 7;});
|
||||
// 获得 task 的 future
|
||||
std::future<int> result = task.get_future(); // 在一个线程中执行 task
|
||||
std::thread(std::move(task)).detach(); std::cout << "Waiting...";
|
||||
result.wait();
|
||||
// 输出执行结果
|
||||
std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n';
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::thread t(foo);
|
||||
|
||||
foo2();
|
||||
|
||||
t.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
61
7/7.2.cpp
Normal file
61
7/7.2.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// 7.2.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 生产者消费者模型
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
|
||||
int main()
|
||||
{
|
||||
// 生产者数量
|
||||
std::queue<int> produced_nums;
|
||||
// 互斥锁
|
||||
std::mutex m;
|
||||
// 条件变量
|
||||
std::condition_variable cond_var;
|
||||
// 结束标志
|
||||
bool done = false;
|
||||
// 通知标志
|
||||
bool notified = false;
|
||||
|
||||
// 生产者线程
|
||||
std::thread producer([&]() {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// 创建互斥锁
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
std::cout << "producing " << i << '\n';
|
||||
produced_nums.push(i);
|
||||
notified = true;
|
||||
// 通知一个线程
|
||||
cond_var.notify_one();
|
||||
}
|
||||
done = true;
|
||||
cond_var.notify_one();
|
||||
});
|
||||
// 消费者线程
|
||||
std::thread consumer([&]() {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
while (!done) {
|
||||
while (!notified) { // 循环避免虚假唤醒
|
||||
cond_var.wait(lock);
|
||||
}
|
||||
while (!produced_nums.empty()) {
|
||||
std::cout << "consuming " << produced_nums.front() << '\n';
|
||||
produced_nums.pop();
|
||||
}
|
||||
notified = false;
|
||||
}
|
||||
});
|
||||
|
||||
producer.join();
|
||||
consumer.join();
|
||||
}
|
||||
50
8/8.1.cpp
Normal file
50
8/8.1.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// 8.1.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// noexcept
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void may_throw() {
|
||||
throw true;
|
||||
}
|
||||
auto non_block_throw = []{
|
||||
may_throw();
|
||||
};
|
||||
void no_throw() noexcept {
|
||||
return;
|
||||
}
|
||||
|
||||
auto block_throw = []() noexcept {
|
||||
no_throw();
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha
|
||||
<< "may_throw() noexcept? " << noexcept(may_throw()) << std::endl
|
||||
<< "no_throw() noexcept? " << noexcept(no_throw()) << std::endl
|
||||
<< "lmay_throw() noexcept? " << noexcept(non_block_throw()) << std::endl
|
||||
<< "lno_throw() noexcept? " << noexcept(block_throw()) << std::endl;
|
||||
|
||||
try {
|
||||
may_throw();
|
||||
} catch (...) {
|
||||
std::cout << "捕获异常, 来自 my_throw()" << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
non_block_throw();
|
||||
} catch (...) {
|
||||
std::cout << "捕获异常, 来自 non_block_throw()" << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
block_throw();
|
||||
} catch (...) {
|
||||
std::cout << "捕获异常, 来自 block_throw()" << std::endl;
|
||||
}
|
||||
}
|
||||
33
8/8.2.cpp
Normal file
33
8/8.2.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// 8.2.cpp
|
||||
// c++1x tutorial
|
||||
//
|
||||
// created by changkun at shiyanlou.com
|
||||
//
|
||||
// 字面量
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
std::string operator"" _wow1(const char *wow1, size_t len) {
|
||||
return std::string(wow1)+"woooooooooow, amazing";
|
||||
}
|
||||
|
||||
std::string operator""_wow2 (unsigned long long i) {
|
||||
return std::to_string(i)+"woooooooooow, amazing";
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::string str = R"(C:\\What\\The\\Fxxk)";
|
||||
std::cout << str << std::endl;
|
||||
|
||||
int value = 0b1001010101010;
|
||||
std::cout << value << std::endl;
|
||||
|
||||
|
||||
auto str = "abc"_wow1;
|
||||
auto num = 1_wow2;
|
||||
std::cout << str << std::endl;
|
||||
std::cout << num << std::endl;
|
||||
return 0;
|
||||
}
|
||||
7
9/9.1.cpp
Normal file
7
9/9.1.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
int main() {
|
||||
std::list<int> l = {1, 2, 3};
|
||||
std::sort(l.begin(), l.end());
|
||||
return 0;
|
||||
}
|
||||
131
README.md
131
README.md
@@ -1,2 +1,129 @@
|
||||
# cpp1x-tutorial
|
||||
《C++11/14 高速上手教程》相关源码, http://cpp.ouchangkun.com
|
||||
《高速上手 C++11/14》相关源码
|
||||
====
|
||||
|
||||
**本仓库包含了《[高速上手 C++11/14](http://cpp.ouchangkun.com)》中的相关代码。**
|
||||
|
||||

|
||||
|
||||
本书目的
|
||||
--
|
||||
|
||||
本教程虽号称高速上手教程,但实际上对 C++11/14 的相关特性做了一个较为全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容,这从某种意义上来说,也算是高速上手了。
|
||||
|
||||
这些特性并不需要全部掌握,只需针对特定的应用场景,学习、查阅最适合自己的新特性即可。
|
||||
|
||||
值得一提的是,本教程在介绍这些特性的过程中,尽可能简单明了的介绍了这些特性产生的历史背景和技术需求,这为理解这些特性、运用这些特性提供了很大的帮助。
|
||||
|
||||
目标读者
|
||||
--
|
||||
|
||||
1. 本教程假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解 `Modern C++` 特性的人非常适合阅读本书;
|
||||
2. 本教程一定程度上介绍了一些 C++1x 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习 C++1x 的读者,本教程的定位系**C++1x 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 `Modern C++` 的熟悉度。
|
||||
|
||||
目录
|
||||
--
|
||||
- **第一章 C++11/14 简介**
|
||||
+ 概述
|
||||
+ 被弃用的特性
|
||||
+ 与 C 的兼容性
|
||||
- **第二章 语言可用性的强化**
|
||||
+ `nullptr` 与 `constexpr`
|
||||
+ 类型推导
|
||||
+ `auto`
|
||||
+ `decltype`
|
||||
+ 尾返回类型、`auto` 与 `decltype` 配合
|
||||
|
||||
<!--+ decltype(auto) (C++14)-->
|
||||
|
||||
+ 区间迭代
|
||||
+ 基于范围的 for 循环
|
||||
+ 初始化列表
|
||||
+ `std::initializer_list`
|
||||
+ 统一初始化语法
|
||||
+ 模板增强
|
||||
+ 外部模板
|
||||
+ 尖括号 `>`
|
||||
+ 类型别名模板
|
||||
+ 变长参数模板
|
||||
+ 面向对象增强
|
||||
+ 委托构造
|
||||
+ 继承构造
|
||||
+ 显式虚函数重载
|
||||
+ `override`
|
||||
+ `final`
|
||||
+ 显式禁用默认函数
|
||||
+ 强类型枚举
|
||||
- **第三章 语言运行期的强化**
|
||||
+ lambda 表达式
|
||||
+ lambda 表达式基础
|
||||
+ 值捕获
|
||||
+ 引用捕获
|
||||
+ 隐式捕获
|
||||
+ 表达式捕获
|
||||
+ 泛型 lambda
|
||||
+ 函数对象包装器
|
||||
+ std::function
|
||||
+ std::bind/std::placeholder
|
||||
+ 右值引用
|
||||
+ 左值、右值的纯右值、将亡值、右值
|
||||
+ 右值引用和左值引用
|
||||
+ 移动语义
|
||||
+ 完美转发
|
||||
- **第四章 对标准库的扩充: 新增容器**
|
||||
+ `std::array`
|
||||
+ `std::forward_list`
|
||||
+ `std::unordered_set`
|
||||
+ `std::unordered_map`
|
||||
+ `std::tuple`
|
||||
+ 基本操作
|
||||
+ 运行期索引
|
||||
+ 合并与迭代
|
||||
- **第五章 对标准库的扩充: 智能指针和引用计数**
|
||||
+ RAII 与引用计数
|
||||
+ `std::shared_ptr`
|
||||
+ `std::unique_ptr`
|
||||
+ `std::weak_ptr`
|
||||
- **第六章 对标准库的扩充: 正则表达式库**
|
||||
+ 正则表达式简介
|
||||
+ 普通字符
|
||||
+ 特殊字符
|
||||
+ 限定符
|
||||
+ `std::regex` 及其相关
|
||||
+ `std::regex`
|
||||
+ `std::regex_match`
|
||||
+ `std::match_results`
|
||||
- **第七章 对标准库的扩充: 语言级线程支持**
|
||||
+ `std::thread`
|
||||
+ `std::mutex`
|
||||
+ `std::unique_lock`
|
||||
+ `std::future`
|
||||
+ `std::packaged_task`
|
||||
+ `std::condition_variable`
|
||||
- **第八章 其他杂项**
|
||||
+ 新类型
|
||||
+ `long long int`
|
||||
+ `noexcept` 的修饰和操作
|
||||
+ 字面量
|
||||
+ 原始字符串字面量
|
||||
+ 自定义字面量
|
||||
- **第九章 扩展主题: C++17 简介**
|
||||
+ 主要入选特性
|
||||
+ 非类型模板参数的 `auto`
|
||||
+ `std::variant<>`
|
||||
+ 结构化绑定(Structured bindings)
|
||||
+ 变量声明的强化
|
||||
+ 未入选特性
|
||||
+ Concepts
|
||||
|
||||
## 交流
|
||||
|
||||
1. 本教程在每节的最下方提供了评论,如果读者发现教程中内容的错误,可以使用评论或者 issue
|
||||
来指出;
|
||||
2. 本书依然有很多特性没有参与介绍,例如 `alignas` 内存对齐、无限制联合等,主要是考虑到这些特性的使用频次实在是太低,故没有多做介绍,若对未提及的特性有需求,笔者可以考虑将其加入`第八章`;
|
||||
3. 本教程有以下交流群,有兴趣的读者可以加入,加群请注明 `github`:
|
||||
|
||||

|
||||
|
||||
## 许可
|
||||
|
||||
本项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。
|
||||
|
||||
BIN
oreilly-cover.jpg
Normal file
BIN
oreilly-cover.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 615 KiB |
BIN
qq-group.png
Normal file
BIN
qq-group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user