constexpr
functionsexchange
make_unique
optional
shared_mutex
and shared_lock
dynarray
return
statement, you can not specify the type of the return value - the compiler can output it independently. Many were surprised that this feature is not available for normal functions. The corresponding proposal was made before the release of C ++ 11, but it was postponed due to time constraints, and now, several years later, it has been added to the standard. Also, this amendment says that if a function or lambda expression has several return
that return the same type, in this case, the compiler must also output the type value automatically, including the case with a recursive call.
auto iterate(int len) // - int { for (int i = 0; i < len; ++i) if (search (i)) return i; return -1; } auto h() { return h(); } // , auto sum(int i) { if (i == 1) return i; // - int else return sum(i-1)+i; // } template <class T> auto f(T t) { return t; } // []()->auto& { return f(); } //
// foo.h class Foo { public: auto getA() const; }; // foo.cpp #include "foo.h" auto Foo::getA() const { return something; } // main.cpp #include "foo.h" int main() { Foo bar; auto a = bar.getA(); // , }
std::initializer_list
.
#include <memory> #include <iostream> #include <utility> template <class T> void run(T&& runnable) { runnable(); }; int main() { std::unique_ptr<int> result(new int{42}); run([result](){std::cout << *result << std::endl;}); }
[ x { move(x) }, y = transform(y, z), foo, bar, baz ] { ... }
x
will be directly initialized by moving x
, y
will be initialized by the result of the call transform
, and the rest will be captured by value. Another example:
int x = 4; auto y = [&r = x, x = x+1]()->int { r += 2; return x+2; }(); // ::x 6, y 7-.
[int x = get_x()] { ... } [Container y{get_container()}] { ... } [int z = 5] { ... }
[&&x] { ... }
auto
type specifier meaning a generic (template) lambda parameter void f1(int (*)(int)) { } void f2(char (*)(int)) { } void g(int (*)(int)) { } // #1 void g(char (*)(char)) { } // #2 void h(int (*)(int)) { } // #3 void h(char (*)(int)) { } // #4 auto glambda = [](auto a) { return a; }; f1(glambda); // OK f2(glambda); // : ID g(glambda); // : h(glambda); // OK: #3, ID int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK
auto vglambda = [](auto printer) { return [=](auto&& ... ts) { // OK: ts - printer(std::forward<decltype(ts)>(ts)...); }; }; auto p = vglambda( [](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; } ); p(1, 'a', 3.14); // OK: 1a3.14
constexpr
functionsconstexpr
functions:
static
, thread_local
and uninitialized variables)if
and switch
(but not goto
)for
(including range-based for
), while
and do-while
constexpr
calculations constexpr int abs(int x) { if (x < 0) x = -x; return x; // OK } constexpr int first(int n) { static int value = n; // : return value; } constexpr int uninit() { int a; // : return a; } constexpr int prev(int x) { return --x; } // OK constexpr int g(int x, int n) { // OK int r = 1; while (--n > 0) r *= x; return r; }
constexpr
non-static member functions implicitly obtained the const
specifier was removed (for more details, click here ).
constexpr
variable templates for more convenient combination with template algorithms (it is possible to use not only with built-in types, but also with user-defined types):
template<typename T> constexpr T pi = T(3.1415926535897932385); template<typename T> T circular_area(T r) { return pi<T> * r * r; } struct matrix_constants { template<typename T> using pauli = hermitian_matrix<T, 2>; template<typename T> constexpr pauli<T> sigma1 = { { 0, 1 }, { 1, 0 } }; template<typename T> constexpr pauli<T> sigma2 = { { 0, -1i }, { 1i, 0 } }; template<typename T> constexpr pauli<T> sigma3 = { { 1, 0 }, { -1, 0 } }; };
exchange
atomic_exchange
function, which allows you to assign a new value to the object and return its old value. This function can be useful for ordinary objects.
// template<typename T, typename U=T> T exchange(T& obj, U&& new_val) { T old_val = std::move(obj); obj = std::forward<U>(new_val); return old_val; }
std::unique_ptr::reset
:
template<typename T, typename D> void unique_ptr<T, D>::reset(pointer p = pointer()) { pointer old = ptr_; ptr_ = p; if (old) deleter_(old); }
template<typename T, typename D> void unique_ptr<T, D>::reset(pointer p = pointer()) { if (pointer old = std::exchange(ptr_, p)) deleter_(old); }
make_unique
make_shared
. It allows you to optimize the allocation of memory for shared_ptr
, as well as to increase security regarding exceptions. Although a similar optimization for unique_ptr
not possible, increased security with respect to exceptions never unique_ptr
. For example, here, in both cases, we can get a memory leak if an exception is thrown during the creation of one object, and the second object has already been created but not yet placed in unique_ptr
:
void foo(std::unique_ptr<A> a, std::unique_ptr<B> b); int main() { foo(new A, new B); foo(std::unique_ptr<A>{new A}, foo(std::unique_ptr<B>{new B}); }
make_unique
. Thus, C ++ 14 almost completely (with the exception of very rare cases) suggests that programmers abandon the new
and delete
operators.
#include <iostream> #include <string> #include <memory> using namespace std; void foo(std::unique_ptr<string> a, std::unique_ptr<string> b) {} int main() { cout << *make_unique<int>() << endl; cout << *make_unique<int>(1729) << endl; cout << "\"" << *make_unique<string>() << "\"" << endl; cout << "\"" << *make_unique<string>("meow") << "\"" << endl; cout << "\"" << *make_unique<string>(6, 'z') << "\"" << endl; auto up = make_unique<int[]>(5); for (int i = 0; i < 5; ++i) { cout << up[i] << " "; } cout << endl; foo(make_unique<string>(), make_unique<string>()); // auto up1 = make_unique<string[]>("error"); // auto up2 = make_unique<int[]>(10, 20, 30, 40); // auto up3 = make_unique<int[5]>(); // auto up4 = make_unique<int[5]>(11, 22, 33, 44, 55); // } /* Output: 0 1729 "" "meow" "zzzzzz" 0 0 0 0 0 */
std::stringstream ss; std::string original = "foolish me"; std::string round_trip; ss << original; ss >> round_trip; std::cout << original; // : foolish me std::cout << round_trip; // : foolish assert(original == round_trip); // assert
std::stringstream ss; std::string original = "foolish me"; std::string round_trip; ss << quoted(original); ss >> quoted(round_trip); std::cout << original; // : foolish me std::cout << round_trip; // : foolish me assert(original == round_trip); // assert
std::cout << "She said \"Hi!\""; // : She said "Hi!" std::cout << quoted("She said \"Hi!\""); // : "She said \"Hi!\""
s
operator for basic_string
h
, min
, s
, ms
, us
, ns
for types included in chrono :: duration auto mystring = "hello world"s; // std::string auto mytime = 42ns; // chrono::nanoseconds
s
do not conflict, since they accept different types of parameters.
optional
optional<int> str2int(string); // , int get_int_form_user() { string s; for (;;) { cin >> s; optional<int> o = str2int(s); // 'o' , if (o) { // 'o' ? return *o; // } } }
shared_mutex
and shared_lock
shared_mutex
library designed for this purpose. The lock
function provides unique access, and can be used with lock_guard
and unique_lock
previously added. To get shared access, you need to use the lock_shared
function, and it’s better to do this through the shared_lock
/ RAII class shared_lock
:
using namespace std; shared_mutex rwmutex; { shared_lock<shared_mutex> read_lock(rwmutex); // } { unique_lock<shared_mutex> write_lock(rwmutex); // lock_guard // }
dynarray
std::dynarray
, which also recognizes its size at run time and cannot change it further, however, it can either place objects on the stack, or place them in a heap, while having an interface in many ways similar to std::array
and std::vector
. According to the amendment, this class can be explicitly optimized by the compiler, for use cases of the stack.
dynarray
and optional
have been moved to separate technical specifications.
Source: https://habr.com/ru/post/184606/