You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
book: add 'since C++NN' markers to Chapter 2 features
Add an italic standard-version marker under each feature heading in
Chapter 2 (both languages), so a reader who already knows some C++11 can
see at a glance what is newer. Same convention introduced with the
structured-bindings demo.
Copy file name to clipboardExpand all lines: book/en-us/02-usability.md
+58Lines changed: 58 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,6 +18,8 @@ which refers to the language behavior that occurred before the runtime.
18
18
19
19
### nullptr
20
20
21
+
*(since C++11)*
22
+
21
23
The purpose of `nullptr` appears to replace `NULL`. There are **null pointer constants** in the C and C++ languages,
22
24
which can be implicitly converted to null pointer value of any pointer type,
23
25
or null member pointer value of any pointer-to-member type in C++.
@@ -93,6 +95,8 @@ We will discuss them in detail later in the [decltype](#decltype) section.
93
95
94
96
### constexpr
95
97
98
+
*(since C++11; relaxed in C++14)*
99
+
96
100
C++ itself already has the concept of constant expressions, such as 1+2,
97
101
3\*4. Such expressions always produce the same result without any side effects.
98
102
If the compiler can directly optimize and embed these expressions into the program at
@@ -188,6 +192,8 @@ constexpr int fibonacci(const int n) {
188
192
189
193
### if-switch
190
194
195
+
*(since C++17)*
196
+
191
197
In traditional C++, the declaration of a variable can declare a temporary variable `int`
192
198
even though it can be located anywhere, even within a `for` statement,
193
199
but there is always no way to declare a temporary variable in the `if` and `switch` statements.
@@ -237,6 +243,8 @@ Is it similar to the Go?
237
243
238
244
### Initializer list
239
245
246
+
*(since C++11)*
247
+
240
248
Initialization is a very important language feature,
241
249
the most common one is when the object is initialized.
242
250
In traditional C++, different objects have different initialization methods,
@@ -378,6 +386,8 @@ C++11 introduces the two keywords `auto` and `decltype` to implement type deriva
378
386
379
387
### auto
380
388
389
+
*(since C++11)*
390
+
381
391
`auto` has been in C++ for a long time, but it always exists as an indicator of a storage type, coexisting with `register`. In traditional C++, if a variable is not declared as a `register` variable, it is automatically treated as an `auto` variable. And with `register` being deprecated (used as a reserved keyword in C++17 and later used, it doesn't currently make sense), the semantic change to `auto` is very natural.
382
392
383
393
One of the most common and notable examples of type derivation using `auto` is the iterator. You should see the lengthy iterative writing in traditional C++ in the previous section:
The `decltype` keyword is used to solve the defect that the `auto` keyword
458
470
can only deduce the type of a variable. Its usage is very similar to `typeof`,
459
471
a non-standard extension long provided by some compilers (e.g. GCC and Clang)
@@ -495,6 +507,8 @@ type z == type x
495
507
496
508
### tail type inference
497
509
510
+
*(since C++11)*
511
+
498
512
You may think that whether `auto` can be used to deduce the return type of a function. Still consider an example of an add function, which we have to write in traditional C++:
As we saw at the beginning of this chapter, we know that C++11 introduces the `constexpr` keyword, which compiles expressions or functions into constant results. A natural idea is that if we introduce this feature into the conditional judgment, let the code complete the branch judgment at compile-time, can it make the program more efficient? C++17 introduces the `constexpr` keyword into the `if` statement, allowing you to declare the condition of a constant expression in your code. Consider the following code:
608
626
609
627
```cpp
@@ -640,6 +658,8 @@ int main() {
640
658
641
659
### Range-based for loop
642
660
661
+
*(since C++11)*
662
+
643
663
Finally, C++11 introduces a range-based iterative method, and we can write loops that are as concise
644
664
as Python, and we can further simplify the previous example:
645
665
@@ -689,6 +709,8 @@ C++ templates have always been a special art of the language, and templates can
689
709
690
710
### Extern templates
691
711
712
+
*(since C++11)*
713
+
692
714
In traditional C++, templates are instantiated by the compiler only when they are used. In other words, as long as a fully defined template is encountered in the code compiled in each compilation unit (file), it will be instantiated. This results in an increase in compile time due to repeated instantiations. Also, we have no way to tell the compiler not to trigger the instantiation of the template.
693
715
694
716
To this end, C++11 introduces an external template that extends the syntax of the original mandatory compiler to instantiate a template at a specific location, allowing us to explicitly tell the compiler when to instantiate the template:
@@ -700,6 +722,8 @@ extern template class std::vector<double>; // should not instantiation in curren
700
722
701
723
### The ">"
702
724
725
+
*(since C++11)*
726
+
703
727
In the traditional C++ compiler, `>>` is always treated as a right shift operator. But actually we can easily write the code for the nested template:
704
728
705
729
```cpp
@@ -723,6 +747,8 @@ std::vector<MagicType<(1>2)>> magic; // legal, but not recommended
723
747
724
748
### Type alias templates
725
749
750
+
*(since C++11)*
751
+
726
752
Before you understand the type alias template, you need to understand the difference between "template" and "type". Carefully understand this sentence: **Templates are used to generate types.** In traditional C++, `typedef` can define a new name for the type, but there is no way to define a new name for the template. Because the template is not a type. E.g:
727
753
728
754
```cpp
@@ -755,6 +781,8 @@ int main() {
755
781
756
782
### Variadic templates
757
783
784
+
*(since C++11)*
785
+
758
786
The template has always been one of C++'s unique **Black Magic**.
759
787
In traditional C++,
760
788
both a class template and a function template could only accept
@@ -874,6 +902,8 @@ To avoid compiler warnings, we can explicitly convert `std::initializer_list` to
874
902
875
903
### Fold expression
876
904
905
+
*(since C++17)*
906
+
877
907
In C++ 17, this feature of the variable length parameter is further brought to the expression, consider the following example:
878
908
879
909
```cpp
@@ -889,6 +919,8 @@ int main() {
889
919
890
920
### Non-type template parameter deduction
891
921
922
+
*(since C++17)*
923
+
892
924
What we mainly mentioned above is a form of template parameters: type template parameters.
893
925
894
926
```cpp
@@ -935,6 +967,8 @@ int main() {
935
967
936
968
### SFINAE and `std::enable_if`
937
969
970
+
*(since C++11)*
971
+
938
972
SFINAE stands for "Substitution Failure Is Not An Error". It describes the rule that, when substituting template arguments produces an invalid type or expression in the **immediate context**, the compiler does not raise an error but silently removes that candidate from the overload set. This was the main way to constrain template parameters before C++20's concepts.
939
973
940
974
The most common tool is `std::enable_if` from `<type_traits>`. The `describe` below is visible only for integral types:
@@ -969,6 +1003,8 @@ SFINAE is powerful but obscure to write and produces verbose error messages. C++
969
1003
970
1004
### Delegate constructor
971
1005
1006
+
*(since C++11)*
1007
+
972
1008
C++11 introduces the concept of a delegate construct, which allows a constructor to call another constructor
973
1009
in a constructor in the same class, thus simplifying the code:
974
1010
@@ -995,6 +1031,8 @@ int main() {
995
1031
996
1032
### Inheritance constructor
997
1033
1034
+
*(since C++11)*
1035
+
998
1036
In traditional C++, constructors need to pass arguments one by one if they need inheritance, which leads to inefficiency. C++11 introduces the concept of inheritance constructors using the keyword using:
999
1037
1000
1038
```cpp
@@ -1023,6 +1061,8 @@ int main() {
1023
1061
1024
1062
### Explicit virtual function overwrite
1025
1063
1064
+
*(since C++11)*
1065
+
1026
1066
In traditional C++, it is often prone to accidentally overloading virtual functions. E.g:
1027
1067
1028
1068
```cpp
@@ -1074,6 +1114,8 @@ struct SubClass3: Base {
1074
1114
1075
1115
### Explicit delete default function
1076
1116
1117
+
*(since C++11)*
1118
+
1077
1119
In traditional C++, if the programmer does not provide it, the compiler will default to generating default constructors, copy constructs, assignment operators, and destructors for the object. Besides, C++ also defines operators such as `new` `delete` for all classes. This part of the function can be overridden when the programmer needs it.
1078
1120
1079
1121
This raises some requirements: the ability to accurately control the generation of default functions cannot be controlled. For example, when copying a class is prohibited, the copy constructor and the assignment operator must be declared as `private`. Trying to use these undefined functions will result in compilation or link errors, which is a very unconventional way.
@@ -1093,6 +1135,8 @@ class Magic {
1093
1135
1094
1136
### Strongly typed enumerations
1095
1137
1138
+
*(since C++11)*
1139
+
1096
1140
In traditional C++, enumerated types are not type-safe, and enumerated types are treated as integers, which allows two completely different enumerated types to be directly compared (although the compiler gives the check, but not all), ** Even the enumeration value names of different enum types in the same namespace cannot be the same**, which is usually not what we want to see.
1097
1141
1098
1142
C++11 introduces an enumeration class and declares it using the syntax of `enum class`:
Before C++17, a non-const static data member of a class had to be defined separately outside the class, and defining a global variable in a header would cause duplicate-definition link errors when the header was included by multiple translation units. C++17 introduces `inline` variables, which allow a variable (including a static data member) to be defined in a header without violating the One Definition Rule (ODR), even when included by multiple translation units:
1143
1189
1144
1190
```cpp
@@ -1152,6 +1198,8 @@ This greatly simplifies writing header-only libraries.
1152
1198
1153
1199
### Nested namespace definitions
1154
1200
1201
+
*(since C++17)*
1202
+
1155
1203
C++17 allows writing nested namespace definitions in a single line using `::`, instead of indenting level by level:
1156
1204
1157
1205
```cpp
@@ -1172,6 +1220,8 @@ namespace A::B::C {
1172
1220
1173
1221
### constexpr lambda
1174
1222
1223
+
*(since C++17)*
1224
+
1175
1225
Since C++17, a lambda expression that satisfies the requirements of a constant expression is implicitly `constexpr` (and may also be explicitly marked `constexpr`), so it can be evaluated at compile time:
1176
1226
1177
1227
```cpp
@@ -1183,6 +1233,8 @@ constexpr int result = add(3, 4); // evaluated at compile time, result == 7
1183
1233
1184
1234
### Single-argument static_assert
1185
1235
1236
+
*(since C++17)*
1237
+
1186
1238
`static_assert` performs a compile-time assertion. Before C++17 it required a diagnostic message as its second argument; since C++17 that message is optional:
1187
1239
1188
1240
```cpp
@@ -1192,6 +1244,8 @@ static_assert(sizeof(int) >= 2, "int must be >= 2 bytes"); // a message is still
1192
1244
1193
1245
### New aggregate rules
1194
1246
1247
+
*(since C++17)*
1248
+
1195
1249
C++17 relaxed the definition of an aggregate: an aggregate may now have public base classes (which must themselves be aggregates), and the base subobject can be brace-initialized along with the rest:
1196
1250
1197
1251
```cpp
@@ -1203,6 +1257,8 @@ Derived d{{1}, 2}; // {a}, b — legal since C++17
1203
1257
1204
1258
### Boolean logic metafunctions
1205
1259
1260
+
*(since C++17)*
1261
+
1206
1262
C++17 added `std::conjunction`, `std::disjunction`, and `std::negation` to `<type_traits>` for composing other type traits with logical AND/OR/NOT at compile time (and `conjunction`/`disjunction` short-circuit):
C++17 standardized the preprocessor operator `__has_include`, which checks at compile time whether a header is available, enabling portable conditional inclusion:
0 commit comments