@@ -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
785785template <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
810810template <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
830830template <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
840840template <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
887889cout << " 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);
943950std::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
0 commit comments