Skip to content

Commit e1bfd92

Browse files
committed
add-bind-and-tricks
1 parent 8d4aa5d commit e1bfd92

File tree

5 files changed

+640
-26
lines changed

5 files changed

+640
-26
lines changed

docs/cmake_tutor.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 学现代 C++ 从现代 CMake 学起(未完工)
2+
3+
```cmake
4+
TODO
5+
```

docs/cpp_tricks.md

+85-18
Original file line numberDiff line numberDiff line change
@@ -763,73 +763,73 @@ p = &j; // 错误:p 本身也不可变,不能改变指向
763763
大家都知道,函数的返回类型可以声明为 `auto`,让其自动推导。
764764

765765
```cpp
766-
auto func() { // int func();
766+
auto square() { // int square();
767767
return 1;
768768
}
769769
```
770770

771771
但你知道从 C++20 开始,参数也可以声明为 auto 了吗?
772772

773773
```cpp
774-
auto func(auto x) { // T func(T x);
774+
auto square(auto x) { // T square(T x);
775775
return x * x;
776776
}
777777

778-
func(1); // func(int)
779-
func(3.14); // func(double)
778+
square(1); // square(int)
779+
square(3.14); // square(double)
780780
```
781781
782782
等价于以下“模板函数”的传统写法:
783783
784784
```cpp
785785
template <typename T>
786-
T func(T x) {
786+
T square(T x) {
787787
return x * x;
788788
}
789789
790-
func(1); // func<int>(int)
791-
func(3.14); // func<double>(double)
790+
square(1); // square<int>(int)
791+
square(3.14); // square<double>(double)
792792
```
793793

794794
因为是模板函数,所以也很难分离声明和定义,只适用于头文件中就地定义函数的情况。
795795

796796
`auto` 参数还可以带有引用:
797797

798798
```cpp
799-
auto func(auto const &x) { // T func(T const &x);
799+
auto square(auto const &x) { // T square(T const &x);
800800
return x * x;
801801
}
802802

803-
func(1); // func(int const &)
804-
func(3.14); // func(double const &)
803+
square(1); // square(int const &)
804+
square(3.14); // square(double const &)
805805
```
806806
807807
等价于:
808808
809809
```cpp
810810
template <typename T>
811-
T func(T const &x) {
811+
T square(T const &x) {
812812
return x * x;
813813
}
814814
```
815815

816816
`auto` 参数最好的配合莫过于是与同样 C++20 引入的 concept:
817817

818818
```cpp
819-
auto func(std::integral auto x) { // T func(T x) requires std::integral<T>
819+
auto square(std::integral auto x) { // T square(T x) requires std::integral<T>
820820
return x * x;
821821
}
822822

823-
func(1); // func(int)
824-
func(3.14); // 错误:double 不是整数类型
823+
square(1); // square(int)
824+
square(3.14); // 错误:double 不是整数类型
825825
```
826826
827827
等价于:
828828
829829
```cpp
830830
template <typename T>
831831
requires std::integral<T>
832-
T func(T x) {
832+
T square(T x) {
833833
return x * x;
834834
}
835835
```
@@ -838,7 +838,7 @@ T func(T x) {
838838

839839
```cpp
840840
template <std::integral T>
841-
T func(T x) {
841+
T square(T x) {
842842
return x * x;
843843
}
844844
```
@@ -881,12 +881,19 @@ cout.flush();
881881

882882
如果还用 endl 的话,就相当于刷新了两次,浪费性能。
883883

884-
所以,我们只需要输出 `'\n'` 就可以了,每次换行时 cout 都会自动刷新,endl 是一个典型的以讹传讹错误写法。
884+
可见,endl 是一个被很多无脑教材错误宣传,实际上根本多此一举的东西。
885+
886+
我们只需要输出 `'\n'` 就可以了,每次换行时 cout 都会自动刷新。
885887

886888
```cpp
887889
cout << "Hello, World!" << '\n';
888890
```
889891

892+
endl 是一个典型的以讹传讹错误写法,只有当你的输出是指向另一个进程的管道时,其附带的刷新功能才有作用。
893+
894+
- 当输出是管道时,`cout` 需要 `endl` 才能刷新。
895+
- 当输出是普通控制台时,`cout` 需要 `endl` 才能刷新。
896+
890897
## 多线程中 cout 出现乱序?
891898

892899
同学:小彭老师,我在多线程环境中使用:
@@ -943,6 +950,66 @@ cout << std::format("the answer is {}\n", 42);
943950
std::println("the answer is {}", 42);
944951
```
945952
953+
## cerr 与 cout 的抉择
954+
955+
如果你的目的是调试和报错,可以考虑用 `cerr`!
956+
957+
他会在每次 `<<` 时刷新,`cerr` 才是最适合打印错误和调试信息的流。
958+
959+
`cout` 的优点是不需要时刻刷新,有更好的性能。
960+
961+
```cpp
962+
cout << "hello\n";
963+
cout << "the answer is ";
964+
cout << 42;
965+
*(int *)1 = 1; // 崩溃!
966+
cout << "!\n"; // 因为还没有抵达 \n 产生刷新就崩溃,导致之前尚未刷新的 the answer is 42 丢失
967+
```
968+
969+
可能的输出:
970+
971+
```
972+
hello[换行]
973+
```
974+
975+
```cpp
976+
cerr << "hello\n";
977+
cerr << "the answer is ";
978+
cerr << 42;
979+
*(int *)1 = 1; // 崩溃!
980+
cerr << "!\n";
981+
```
982+
983+
输出:
984+
985+
```
986+
hello[换行]
987+
the answer is 42
988+
```
989+
990+
还有一个特点:`cout` 输出到“标准输出流”,可以被输出重定向到文件管道。而 `cerr` 输出到“标准错误流”,通常不会被重定向到文件或管道。
991+
992+
例如,可以把程序预订的计算结果写到 `cout`,把调试和报错信息写到 `cerr`,这样用户就可以通过 `>` 重定向计算结果,而调试和报错信息则正常输出到屏幕上,不受重定向影响。
993+
994+
```cpp
995+
cout << "1 3 5 7\n";
996+
cerr << "ERROR: this is an error message!\n";
997+
cout << "11 13 17 19\n";
998+
```
999+
1000+
```
1001+
$ g++ prime.cpp -o prime
1002+
$ ./prime
1003+
1 3 5 7
1004+
ERROR: this is an error message!
1005+
11 13 17 19
1006+
$ ./prime > output.txt
1007+
ERROR: this is an error message!
1008+
$ cat output.txt
1009+
1 3 5 7
1010+
11 13 17 19
1011+
```
1012+
9461013
## 智能指针防止大对象移动
9471014

9481015
我们说一个类型大,有两种情况。
@@ -1556,7 +1623,7 @@ int main() {
15561623

15571624
> {{ icon.fun }} `BIND` 这个名字是随便取的,取这个名字是为了辱 `std::bind`
15581625

1559-
为了解决 bind 不能捕获多参数重载的情况,C++17 还引入了 `std::bind_front``std::bind_back`,他们不需要 placeholder,但只能用于参数在最前或者最后的特殊情况
1626+
为了解决 bind 不能捕获多参数重载的情况,C++17 还引入了 `std::bind_front``std::bind_back`,他们不需要 placeholder,但只能用于要绑定的参数在最前或者最后的特殊情况
15601627

15611628
其中 `std::bind_front` 对于我们只需要把第一个参数绑定为 `this`,其他参数如数转发的场景,简直是雪中送炭!
15621629

docs/cuda_intro.md

+2
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,5 @@ CUDA 有两套 API:
269269
```
270270

271271
> {{ icon.tip }} 虽然 CUDA 基于 C++(而不是 C 语言),支持所有 C++ 语言特性。但其 CUDA runtime API 依然是仿 C 风格的接口,可能是照顾了部分从 C 语言转过来的土木老哥,也可能是为了方便被第三方二次封装。
272+
273+
TODO: 更多话题

0 commit comments

Comments
 (0)