如果说 C++11 和 C++20 是两个改动大、影响比较深远的"大版本",那么我感觉 C++17 算是一个小版本。(推荐 vs2019,gcc8,clang10,支持 C++17)
C++17 这个版本让语法变的更自然,代码更容易读懂,会让人觉得:
C++ 本该如此
Automatically decomposes packed structures like tuples structs and arrays into individual named variables.
auto [ a, b, c ] = tuple; // or struct or array
Before C++17, template deduction worked for functions but not for classes.
std::pair p(2, 4.5); // deduces to std::pair<int, double>
auto lock = std::lock_guard(mtx); // deduces to std::lock_guard<std::mutex>
new "if statement with initializer".
if (auto val = GetValue(); condition(val))
// on success
else
// on false...
val
is only present in the scope of the if
and the else
clause.
The static-if for C++!
Reduces the need to use SFINAE or tag dispatch.
if constexpr (is_floating_point_v<T>) { }
constexpr can be used in the context of lambdas.
constexpr auto ID = [] (int n) {
return n;
};
static_assert(ID(3) == 3);
With static variables you usually need to declare it in .h file and initialize it in some .cpp file.
in C++17, a variable could be declared inline. It has the same semantics as a function declared inline: it can be defined, identically, in multiple translation units, must be defined in every translation unit in which it is used, and the behavior of the program is as if there was exactly one variable.
Also, note that constexpr variables are inline implicitly.
struct MyClass {
static inline/constexpr const int sValue = 777;
};
The advantage of inline
over constexpr
is that your initialization expression doesn't have to be constexpr.
Automatically deduce type on non-type template parameters.
template<auto value>
void f() { }
f<10>(); // deduces int
With C++11 we got variadic templates. But you had to write several different versions of a function (like one for one parameter, another for two or more parameters).
auto sum() {
return 0;
}
template<typename T1, typename... T>
auto sum(T1 s, T... ts) {
return s + sum(ts...);
}
And with C++17 we can write much simpler code:
template<typename... Args>
auto sum2(Args... args) {
return (args + ...);
}
typename...
defines a template parameter packArgs...
defines a function parameter pack(args + ...)
is a fold expression, meaning (E~1~ + (... + (E~N-1~ + E~N~))), where E~i~ is the i-th element in the pack expansion.&&
: empty pack denotes true
||
: empty pack denotes false
,
: empty pack denotes void()
Allows you to inject names with using-declarations from all types in a parameter pack.
template<class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
Allows you to write: namespace A::B::C { /* … */ }
Rather than: namespace A { namespace B { namespace C {/* … */ }}}
Ignore unknown attributes - compilers which don't support a given attribute will ignore it. Previously it was unspecified.
Using attribute namespaces without repetition – simplifies using attributes from the same namespace
Attributes for namespaces and enumerators – Fixes the spec, so now attributes can be used for most of the declarations, variables, classes, enums, namespaces, enum values, etc.
this pointer is implicitly captured by lambdas inside member functions.
Now you can use *this when declaring a lambda and this will create a copy of the object. Capturing by value might be especially important for async invocation, parallel processing.
auto x { 1 };
will be now deduced as int, but before it was an initializer list
You can now use typename
instead of class
when declaring a template template parameter.
throw()
is deprecated in C++11 and is identical to noexcept(true)
in C++17, and it will be removed in C++20throw(typeid, typeid, ..)
is deprecated in C++11 and removed in C++17.Previously exception specifications for a function didn’t belong to the type of the function, but it will be part of it.
If a class was derived from some other type you couldn’t use aggregate initialization. But now the restriction is removed.
C++11/14 did not specify any mechanism by which over-aligned data can be dynamically allocated (i.e. respecting the alignment of the data).
Now, we get new functions that takes alignment parameters.
Like void* operator new(std::size_t, std::align_val_t);
This feature allows a C++ program to directly, reliably and portably determine whether or not a library header is available for inclusion.
Copy elision (e.g. RVO) was a common compiler optimization, now it’s guaranteed and defined by the standard!
You can now initialize enum class with a fixed underlying type:
enum class Handle : uint32_t {
Invalid = 0
};
Handle h { 42 }; // OK
In expression such as f(a, b, c): the order of evaluation of a, b, c is still unspecified, but any parameter is fully evaluated before the next one is started. Plus other “practical” changes:
f
, h
, g
, i
(previously any order)std::cout << f() << g(h()) << i();
Types of __begin
and __end
iterators (used in the loop) will be different; only the comparison operator is required. This little change improves Range TS experience.
std::auto_ptr
, std::random_shuffle
, and morestd::result_of
. Introduced std::invoke_result
, std::invoke
, std::apply
std::is_same_v
std::optional
merged from booststd::vairant
merged from booststd::any
merged from boost