
The C ++ language is constantly evolving, and as developers of the static analyzer, it is important for us to keep track of all the changes in order to support all the new features of the language. In this review article I would like to share with the reader the most interesting innovations that appeared in C ++ 17, and also to demonstrate them with examples.
Now support for the new standard is being actively added by compiler developers. You can see what is currently supported by the links:
Convolution template parameters (Fold expressions)
First, a few words about what a list convolution is in general (also known as fold, reduce, or accumulate).
Convolution is a function that applies the given combining function to consecutive pairs of items in the list and returns the result. The simplest example is the summation of list elements using convolution:
')
Example from C ++:
std::vector<int> lst = { 1, 3, 5, 7 };
int res = std::accumulate(lst.begin(), lst.end(), 0,
[](int a, int b) { return a + b; });
std::cout << res << '\n'; // 16
,
. :
1 + (3 + (5 + (7 + 0)))
( ) ,
. :
(((0 + 1) + 3) + 5) + 7
, .
C++17 . :
(pack op ...) | |
(… op pack) | |
(pack op… op init) | |
(init op… op pack) | |
op – :
+ - * / % ^ & | ~ = < > << >> += -= *= /= %=
^= &= |= <<= >>= == != <= >= && || , .* ->*
pack – , (parameter pack)
init –
, , , :
// C++17
#include <iostream>
template<typename... Args>
auto Sum(Args... args)
{
return (args + ...);
}
int main()
{
std::cout << Sum(1, 2, 3, 4, 5) << '\n'; // 15
return 0;
}
:
Sum constexpr.
, :
// C++17
#include <iostream>
template<typename... Args>
auto Func(Args... args)
{
return (args + ... + 100);
}
int main()
{
std::cout << Func(1, 2, 3, 4, 5) << '\n'; //115
return 0;
}
C++17 , :
// C++14
#include <iostream>
auto Sum()
{
return 0;
}
template<typename Arg, typename... Args>
auto Sum(Arg first, Args... rest)
{
return first + Sum(rest...);
}
int main()
{
std::cout << Sum(1, 2, 3, 4); // 10
return 0;
}
',' (),
pack , . :
// C++17
#include <iostream>
template<typename T, typename... Args>
void PushToVector(std::vector<T>& v, Args&&... args)
{
(v.push_back(std::forward<Args>(args)), ...);
// :
//v.push_back(std::forward<Args_1>(arg1)),
//v.push_back(std::forward<Args_2>(arg2)),
//....
}
int main()
{
std::vector<int> vct;
PushToVector(vct, 1, 4, 5, 8);
return 0;
}
, variadic templates.
template<auto>
auto non-type template . :
// C++17
template<auto n>
void Func() { /* .... */ }
int main()
{
Func<42>(); // int
Func<'c'>(); // char
return 0;
}
non-type template – . , :
// C++14
template<typename Type, Type n>
void Func() { /* .... */ }
int main()
{
Func<int, 42>();
Func<char, 'c'>();
return 0;
}
C++17 , - :
// C++14
auto p = std::pair<int, char>(10, 'c');
std::make_pair, :
// C++14
auto p = std::make_pair(10, 'c');
, . :
#include <tuple>
#include <array>
template<typename T, typename U>
struct S
{
T m_first;
U m_second;
S(T first, U second) : m_first(first), m_second(second) {}
};
int main()
{
// C++14
std::pair<char, int> p1 = { 'c', 42 };
std::tuple<char, int, double> t1 = { 'c', 42, 3.14 };
S<int, char> s1 = { 10, 'c' };
// C++17
std::pair p2 = { 'c', 42 };
std::tuple t2 = { 'c', 42, 3.14 };
S s2 = { 10, 'c' };
return 0;
}
(
deduction guides). , :
// C++17
#include <iostream>
template<typename T, typename U>
struct S
{
T m_first;
U m_second;
};
// deduction guide
template<typename T, typename U>
S(const T &first, const U &second) -> S<T, U>;
int main()
{
S s = { 42, "hello" };
std::cout << s.m_first << s.m_second << '\n';
return 0;
}
deduction guide.
:
deduction guide ,
S ,
deduction guide .,
std::make_pair,
std::make_tuple, .
Constexpr if
C++17 . , . :
// C++17
#include <iostream>
#include <type_traits>
template <typename T>
auto GetValue(T t)
{
if constexpr (std::is_pointer<T>::value)
{
return *t;
}
else
{
return t;
}
}
int main()
{
int v = 10;
std::cout << GetValue(v) << '\n'; // 10
std::cout << GetValue(&v) << '\n'; // 10
return 0;
}
C++17
SFINAE enable_if:
// C++14
template<typename T>
typename std::enable_if<std::is_pointer<T>::value,
std::remove_pointer_t<T>>::type
GetValue(T t)
{
return *t;
}
template<typename T>
typename std::enable_if<!std::is_pointer<T>::value, T>::type
GetValue(T t)
{
return t;
}
int main()
{
int v = 10;
std::cout << GetValue(v) << '\n'; // 10
std::cout << GetValue(&v) << '\n'; // 10
return 0;
}
,
constexpr if .
Constexpr
C++17
constexpr.
constexpr ,
constexpr.
:
constexpr ,
constexpr, .
constexpr :
// ++17
constexpr int Func(int x)
{
auto f = [x]() { return x * x; };
return x + f();
}
int main()
{
constexpr int v = Func(10);
static_assert(v == 110);
return 0;
}
constexpr :
// C++17
int main()
{
constexpr auto squared = [](int x) { return x * x; };
constexpr int s = squared(5);
static_assert(s == 25);
return 0;
}
*this -
-
*this:
class SomeClass
{
public:
int m_x = 0;
void f() const
{
std::cout << m_x << '\n';
}
void g()
{
m_x++;
}
// ++14
void Func()
{
// const *this
auto lambda1 = [self = *this](){ self.f(); };
// non-const *this
auto lambda2 = [self = *this]() mutable { self.g(); };
lambda1();
lambda2();
}
// ++17
void FuncNew()
{
// const *this
auto lambda1 = [*this](){ f(); };
// non-const *this
auto lambda2 = [*this]() mutable { g(); };
lambda1();
lambda2();
}
};
inline
C++17 inline inline . , inline, ( ) .
inline , . :
( , extern .cpp)
header.h:
#ifndef _HEADER_H
#define _HEADER_H
inline int MyVar = 42;
#endif
source1.h:
#include "header.h"
....
MyVar += 10;
source2.h:
#include "header.h"
....
Func(MyVar);
C++17
MyVar extern .cpp .
(Structured bindings)
, , , , Structured bindings Decomposition declaration.
:
// C++17
#include <set>
int main()
{
std::set<int> mySet;
auto[iter, ok] = mySet.insert(42);
....
return 0;
}
insert() pair<iterator, bool>,
iterator bool,
false, (..
mySet).
C++17
std::tie:
// C++14
#include <set>
#include <tuple>
int main()
{
std::set<int> mySet;
std::set<int>::iterator iter;
bool ok;
std::tie(iter, ok) = mySet.insert(42);
....
return 0;
}
,
iter ok .
, :
// C++17
#include <iostream>
int main()
{
int arr[] = { 1, 2, 3, 4 };
auto[a, b, c, d] = arr;
std::cout << a << b << c << d << '\n';
return 0;
}
,
.
// C++17
#include <iostream>
struct S
{
char x{ 'c' };
int y{ 42 };
double z{ 3.14 };
};
int main()
{
S s;
auto[a, b, c] = s;
std::cout << a << ' ' << b << ' ' << c << ' ' << '\n';
return 0;
}
, range-based :
// C++17
#include <iostream>
#include <map>
int main()
{
std::map<int, char> myMap;
....
for (const auto &[key, value] : myMap)
{
std::cout << "key: " << key << ' ';
std::cout << "value: " << value << '\n';
}
return 0;
}
if switch
C++17 if switch :
if (init; condition)
switch(init; condition)
:
if (auto it = m.find(key); it != m.end())
{
....
}
. :
std::map<int, std::string> myMap;
....
if (auto[it, ok] = myMap.insert({ 2, "hello" }); ok)
{
....
}
__has_include
__has_include , .
(P0061R1). optional :
#if __has_include(<optional>)
#include <optional>
#define have_optional 1
#elif __has_include(<experimental/optional>)
#include <experimental/optional>
#define have_optional 1
#define experimental_optional 1
#else
#define have_optional 0
#endif
[[noreturn]],
[[carries_dependency]] [[deprecated]] C++17 3 :
[[fallthrough]], break case (.. case), .
:
// C++17
switch (i)
{
case 10:
f1();
break;
case 20:
f2();
break;
case 30:
f3();
break;
case 40:
f4();
[[fallthrough]]; //
case 50:
f5();
}
[[nodiscard]], , :
// C++17
[[nodiscard]] int Sum(int a, int b)
{
return a + b;
}
int main()
{
Sum(5, 6); // /
return 0;
}
[[nodiscard]] , ,
[[nodiscard]]:
// C++17
struct [[nodiscard]] NoDiscardType
{
char a;
int b;
};
NoDiscardType Func()
{
return {'a', 42};
}
int main()
{
Func(); // /
return 0;
}
[[maybe_unused]], / , , . :
//
[[maybe_unused]] static void SomeUnusedFunc() { .... }
//
void Foo([[maybe_unused]] int a) { .... }
void Func()
{
//
[[maybe_unused]] int someUnusedVar = 42;
....
}
std::byte
std::byte '' .
char,
unsigned char uint8_t.
std::byte , , . ,
std::byte F(const unsigned char *).
<cstddef> :
enum class byte : unsigned char {};
(Dynamic allocation of over-aligned types)
C++11
alignas, . C++17 ,
alignas . , :
// C++17
struct alignas(32) S
{
int a;
char c;
};
int main()
{
S *objects = new S[10];
....
return 0;
}
C++17 , :
, ,
a,
b,
c,
d:
a.b
a->b
a->*b
a(b1, b2, b3)
b @= a
a[b]
a << b << c
a >> b >> c
,
b1,
b2,
b3 - . :
string s =
"but I have heard it works even if you don't believe in it";
s.replace(0, 4, "")
.replace(s.find("even"), 4, "only")
.replace(s.find(" don't"), 6, "");
assert(s == "I have heard it works only if you believe in it");
«The C++ Programming Language, 4th edition», « ». unspecified behavior, C++17, . ,
find .
.. :
obj.F1(subexr1).F2(subexr2).F3(subexr3).F4(subexr4)
subexr1,
subexr2,
subexr3,
subexr4 F1,
F2,
F3,
F4. , .
Filesystem
C++17 .
boost::filesystem, .
std::filesystem.
:
#include <filesystem>
namespace fs = std::filesystem;
fs::path:
fs::path file_path("/dir1/dir2/file.txt");
cout << file_path.parent_path() << '\n'; // "/dir1/dir2"
cout << file_path.filename() << '\n'; // "file.txt"
cout << file_path.extension() << '\n'; // ".txt"
file_path.replace_filename("file2.txt");
file_path.replace_extension(".cpp");
cout << file_path << '\n'; // "/dir1/dir2/file2.cpp"
fs::path dir_path("/dir1");
dir_path.append("dir2/file.txt");
cout << dir_path << '\n'; // "/dir1/dir2/file.txt"
:
//
fs::path current_path = fs::current_path();
//
fs::create_directory("/dir");
//
fs::create_directories("/dir/subdir1/subdir2");
//
if (fs::exists("/dir/subdir1"))
{
cout << "yes\n";
}
//
for (auto &p : fs::directory_iterator(current_path))
{
cout << p.path() << '\n';
}
//
for (auto &p : fs::recursive_directory_iterator(current_path))
{
cout << p.path() << '\n';
}
//
fs::copy("/dir", "/dir_copy");
//
fs::copy("/dir", "/dir_copy", fs::copy_options::recursive);
// ,
fs::remove_all("/dir");
fs::copy_options :
| |
none | , . ( ) |
skip_existing | , . |
overwrite_existing | . |
update_existing | , . |
:
//
if (fs::exists("/dir/file.txt"))
{
cout << "yes\n";
}
//
fs::copy_file("/dir/file.txt", "/dir/file_copy.txt",
fs::copy_options::overwrite_existing);
// ( )
uintmax_t size = fs::file_size("/dir/file.txt");
//
fs::rename("/dir/file.txt", "/dir/file2.txt");
// ,
fs::remove("/dir/file2.txt");
std::filesystem.
.
std::optional
, . , , , , - :
// ++17
std::optional<int> convert(my_data_type arg)
{
....
if (!fail)
{
return result;
}
return {};
}
int main()
{
auto val = convert(data);
if (val.has_value())
{
std::cout << "conversion is ok, ";
std::cout << "val = " << val.value() << '\n';
}
else
{
std::cout << "conversion failed\n";
}
return 0;
}
std::optional value_or,
optional, .
std::any
std::any . ,
std::any int,
float, . :
#include <string>
#include <any>
int main()
{
std::any a = 42;
a = 11.34f;
a = std::string{ "hello" };
return 0;
}
,
std::any , . ,
std::string, ..
std::any .
,
std::any,
std::any_cast. :
#include <iostream>
#include <string>
#include <any>
int main()
{
std::any a = 42;
std::cout << std::any_cast<int>(a) << '\n';
a = 11.34f;
std::cout << std::any_cast<float>(a) << '\n';
a = std::string{ "hello" };
std::cout << std::any_cast<std::string>(a) << '\n';
return 0;
}
std::any_cast , ,
std::bad_any_cast.
type():
#include <any>
int main()
{
std::any a = 42;
std::cout << a.type().name() << '\n'; // "int"
return 0;
}
std::variant
std::variant — ,
union, , . ,
union,
std::variant non-POD .
#include <iostream>
#include <variant>
int main()
{
// int, float char.
std::variant<int, float, char> v;
v = 3.14f;
v = 42;
std::cout << std::get<int>(v);
//std::cout << std::get<float>(v); // std::bad_variant_access
//std::cout << std::get<char>(v); // std::bad_variant_access
//std::cout << std::get<double>(v); // compile-error
return 0;
}
std::variant std::get.
std::bad_variant_access, .
std::get_if,
std::variant , ,
nullptr :
#include <iostream>
#include <variant>
int main()
{
std::variant<int, float, char> v;
v = 42;
auto ptr = std::get_if<int>(&v);
if (ptr != nullptr)
{
std::cout << "int value: " << *ptr << '\n'; // int value: 42
}
return 0;
}
std::variant std::visit:
#include <iostream>
#include <variant>
int main()
{
std::variant<int, float, char> v;
v = 42;
std::visit([](auto& arg)
{
using Type = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<Type, int>)
{
std::cout << "int value: " << arg << '\n';
}
else if constexpr (std::is_same_v<Type, float>)
{
std::cout << "float value: " << arg << '\n';
}
else if constexpr (std::is_same_v<Type, char>)
{
std::cout << "char value: " << arg << '\n';
}
}, v);
return 0;
}
std::string_view
C++17 –
std::string_view, . ,
std::string_view .
std::string_view ,
std::string,
char[N],
char*, 3 :
// C++14
void Func(const char* str);
void Func(const char str[10]);
void Func(const std::string &str);
// C++17
void Func(std::string_view str);
,
const std::string&, std::string_view, , -. , std::string , std::string_view , , .
const string& string_view ,
const string&.
try_emplace insert_or_assign
C++17
std::map std::unordered_map –
try_emplace insert_or_assign.
emplace,
try_emplace «» move-only , . :
// C++17
#include <iostream>
#include <string>
#include <map>
int main()
{
std::string s1("hello");
std::map<int, std::string> myMap;
myMap.emplace(1, "aaa");
myMap.emplace(2, "bbb");
myMap.emplace(3, "ccc");
//std::cout << s1.empty() << '\n'; // 0
//myMap.emplace(3, std::move(s1));
//std::cout << s1.empty() << '\n'; // 1
//std::cout << s1.empty() << '\n'; // 0
//myMap.try_emplace(3, std::move(s1));
//std::cout << s1.empty() << '\n'; // 0
std::cout << s1.empty() << '\n'; // 0
myMap.try_emplace(4, std::move(s1));
std::cout << s1.empty() << '\n'; // 1
return 0;
}
, - ,
myMap,
try_emplace «»
s1,
emplace.
insert_or_assign , , .
std::pair, / , .
operator[], , :
// C++17
#include <iostream>
#include <string>
#include <map>
int main()
{
std::map<int, std::string> m;
m.emplace(1, "aaa");
m.emplace(2, "bbb");
m.emplace(3, "ccc");
auto[it1, inserted1] = m.insert_or_assign(3, "ddd");
std::cout << inserted1 << '\n'; // 0
auto[it2, inserted2] = m.insert_or_assign(4, "eee");
std::cout << inserted2 << '\n'; // 1
return 0;
}
C++17 , ,
operator[].
C++17 , : -, - .
.
C++17 :
namespace ns1::ns2
{
....
}
:
namespace ns1
{
namespace ns2
{
....
}
}
string::data
C++17
std::string data(), :
// ++17
#include <iostream>
int main()
{
std::string str = "hello";
char *p = str.data();
p[0] = 'H';
std::cout << str << '\n'; // Hello
return 0;
}
.
<algorithm>, , . , execution policy, , .
Execution policy 3- :
- std::execution::seq –
- std::execution::par –
- std::execution::par_unseq –
, , :
#include <iostream>
#include <vector>
#include <algorithm>
....
std::for_each(std::execution::par, vct.begin(), vct.end(),
[](auto &e) { e += 42; });
....
, . , ,
.
std::execution::seq – execution policy, , . ,
std::terminate.
, :
std::reduce –
std::accumulate, , . , execution policy. :
....
// vct
std::reduce(std::execution::par, vct.begin(), vct.end())
....
std::transform_reduce – ,
std::reduce.
std::for_each_n –
std::for_each, n . :
....
std::vector<int> vct = { 1, 2, 3, 4, 5 };
std::for_each_n(vct.begin(), 3, [](auto &e) { e *= 10; });
// vct: {10, 20, 30, 4, 5}
....
std::invoke, is_invocable
std::invoke , , . , , ,
operator(), - :
// C++17
#include <iostream>
#include <functional>
int Func(int a, int b)
{
return a + b;
}
struct S
{
void operator() (int a)
{
std::cout << a << '\n';
}
};
int main()
{
std::cout << std::invoke(Func, 10, 20) << '\n'; // 30
std::invoke(S(), 42); // 42
std::invoke([]() { std::cout << "hello\n"; }); // hello
return 0;
}
std::invoke - . C++17
std::is_invocable:
// C++17
#include <iostream>
#include <type_traits>
void Func() { };
int main()
{
std::cout << std::is_invocable<decltype(Func)>::value << '\n'; // 1
std::cout << std::is_invocable<int>::value << '\n'; // 0
return 0;
}
std::to_chars, std::from_chars
C++17
std::to_chars std::from_chars . C C++,
std::to_chars , , :
// C++17
#include <iostream>
#include <charconv>
int main()
{
char arr[128];
auto res1 = std::to_chars(std::begin(arr), std::end(arr), 3.14f);
if (res1.ec != std::errc::value_too_large)
{
std::cout << arr << '\n';
}
float val;
auto res2 = std::from_chars(std::begin(arr), std::end(arr), val);
if (res2.ec != std::errc::invalid_argument &&
res2.ec != std::errc::result_out_of_range)
{
std::cout << arr << '\n';
}
return 0;
}
std::to_chars to_chars_result:
struct to_chars_result
{
char* ptr;
std::errc ec;
};
ptr – + 1
ec –
std::from_chars from_chars_result:
struct from_chars_result
{
const char* ptr;
std::errc ec;
};
ptr – ,
ec –
, , , , -, .. .
std::as_const
std::as_const :
// C++17
#include <utility>
....
MyObject obj{ 42 };
const MyObject& constView = std::as_const(obj);
....
std::size, std::data std::empty
std::begin,
std::end std::size,
std::data std::empty:
// C++17
#include <vector>
int main()
{
std::vector<int> vct = { 3, 2, 5, 1, 7, 6 };
size_t sz = std::size(vct);
bool empty = std::empty(vct);
auto ptr = std::data(vct);
int a1[] = { 1, 2, 3, 4, 5, 6 };
// C-style .
size_t sz2 = std::size(a1);
return 0;
}
std::clamp
C++17
std::clamp(x, low, high), x, [low, high] :
// C++17
#include <iostream>
#include <algorithm>
int main()
{
std::cout << std::clamp(7, 0, 10) << '\n'; // 7
std::cout << std::clamp(7, 0, 5) << '\n'; //5
std::cout << std::clamp(7, 10, 50) << '\n'; //10
return 0;
}
(
std::gcd) (
std::lcm):
// C++17
#include <iostream>
#include <numeric>
int main()
{
std::cout << std::gcd(24, 60) << '\n'; // 12
std::cout << std::lcm(8, 10) << '\n'; // 40
return 0;
}
(Logical operation metafunctions)
C++17
std::conjunction,
std::disjunction std::negation. , , , .
std::conjunction:
// C++17
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
template<typename... Args>
std::enable_if_t<std::conjunction_v<std::is_integral<Args>...>>
Func(Args... args)
{
std::cout << "All types are integral.\n";
}
template<typename... Args>
std::enable_if_t<!std::conjunction_v<std::is_integral<Args>...>>
Func(Args... args)
{
std::cout << "Not all types are integral.\n";
}
int main()
{
Func(42, true); // All types are integral.
Func(42, "hello"); // Not all types are integral.
return 0;
}
, , ,
std::conjunction std::disjunction , .
, :
// C++17
#include <iostream>
enum E
{
A = 0,
B = 1,
C = 2,
First[[deprecated]] = A,
};
namespace[[deprecated]] DeprecatedFeatures
{
void OldFunc() {};
//....
}
int main()
{
//
DeprecatedFeatures::OldFunc();
//
std::cout << E::First << '\n';
return 0;
}
using
using , . (P0028R4):
// C++14
void f()
{
[[rpr::kernel, rpr::target(cpu, gpu)]]
task();
}
// C++17
void f()
{
[[using rpr:kernel, target(cpu, gpu)]]
task();
}
emplace_back
emplace_back , C++17 :
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vct = { 1, 2, 3 };
auto &r = vct.emplace_back(10);
r = 42;
for (const auto &i : vct)
{
std::cout << i << ' ';
}
}
(Searcher functors)
C++17 , , – — – .
std::search:
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
int main()
{
std::string haystack = "Hello, world!";
std::string needle = "world";
//
auto it1 = std::search(haystack.begin(), haystack.end(),
needle.begin(), needle.end());
auto it2 = std::search(haystack.begin(), haystack.end(),
std::default_searcher(needle.begin(), needle.end()));
// -
auto it3 = std::search(haystack.begin(), haystack.end(),
std::boyer_moore_searcher(needle.begin(), needle.end()));
// - -
auto it4 = std::search(haystack.begin(), haystack.end(),
std::boyer_moore_horspool_searcher(needle.begin(), needle.end()));
std::cout << it1 - haystack.begin() << '\n'; // 7
std::cout << it2 - haystack.begin() << '\n'; // 7
std::cout << it3 - haystack.begin() << '\n'; // 7
std::cout << it4 - haystack.begin() << '\n'; // 7
return 0;
}
std::apply
std::apply allable- , . :
#include <iostream>
#include <tuple>
void Func(char x, int y, double z)
{
std::cout << x << y << z << '\n';
}
int main()
{
std::tuple args{ 'c', 42, 3.14 };
std::apply(Func, args);
return 0;
}
(std::make_from_tuple)
C++17 , , .
std::make_from_tuple:
#include <iostream>
#include <tuple>
struct S
{
char m_x;
int m_y;
double m_z;
S(char x, int y, double z) : m_x(x), m_y(y), m_z(z) {}
};
int main()
{
std::tuple args{ 'c', 42, 3.14 };
S s = std::make_from_tuple<S>(args);
std::cout << s.m_x << s.m_y << s.m_z << '\n';
return 0;
}
std::not_fn (Universal negator not_fn)
C++17
std::not_fn, -.
std::not1 std::not2:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
bool LessThan10(int a)
{
return a < 10;
}
int main()
{
std::vector vct = { 1, 6, 3, 8, 14, 42, 2 };
auto n = std::count_if(vct.begin(), vct.end(),
std::not_fn(LessThan10));
std::cout << n << '\n'; // 2
return 0;
}
(Node handle)
++17 . . :
// C++17
#include <map>
#include <string>
int main()
{
std::map<int, std::string> myMap1{ { 1, "aa" },
{ 2, "bb" },
{ 3, "cc" } };
std::map<int, std::string> myMap2{ { 4, "dd" },
{ 5, "ee" },
{ 6, "ff" } };
auto node = myMap1.extract(2);
myMap2.insert(std::move(node));
// myMap1: {{1, "aa"}, {3, "cc"}}
// myMap2: {{2, "bb"}, {4, "dd"}, {5, "ee"}, {6, "ff"}}
return 0;
}
std::extract ,
insert .
C++17 merge,
extract insert:
// C++17
#include <map>
#include <string>
int main()
{
std::map<int, std::string> myMap1{ { 1, "aa" },
{ 2, "bb" },
{ 3, "cc" } };
std::map<int, std::string> myMap2{ { 4, "dd" },
{ 5, "ee" },
{ 6, "ff" } };
myMap1.merge(myMap2);
// myMap1: {{1, "aa"},
// {2, "bb"},
// {3, "cc"},
// {4, "dd"},
// {5, "ee"},
// {6, "ff"}}
// myMap2: {}
return 0;
}
std::map:
// C++17
#include <map>
#include <string>
int main()
{
std::map<int, std::string> myMap{ { 1, "Tommy" },
{ 2, "Peter" },
{ 3, "Andrew" } };
auto node = myMap.extract(2);
node.key() = 42;
myMap.insert(std::move(node));
// myMap: {{1, "Tommy"}, {3, "Andrew"}, {42, "Peter"}};
return 0;
}
C++17 .
static_assert
static_assert :
static_assert(a == 42, "a must be equal to 42");
static_assert(a == 42); //
static_assert ( constant-expression ) ;
static_assert ( constant-expression , string-literal ) ;
std::*_v<T...>
C++17
<type_traits>,
::value, some_trait_v<T>. , some_trait<T>::value, some_trait_v<T>. :
// C++14
static_assert(std::is_integral<T>::value, "Integral required.");
// C++17
static_assert(std::is_integral_v<T>, "Integral required");
std::shared_ptr for arrays
shared_ptr C-.
T[] shared_ptr delete[] . . :
#include <iostream>
#include <memory>
int main()
{
// C++14
//std::shared_ptr<int[]> arr(new int[7],
// std::default_delete<int[]>());
// C++17
std::shared_ptr<int[]> arr(new int[7]);
arr.get()[0] = 1;
arr.get()[1] = 2;
arr.get()[2] = 3;
....
return 0;
}
std::scoped_lock
C++17
scoped_lock, ( lock) , RAII-. :
#include <thread>
#include <mutex>
#include <iostream>
int var;
std::mutex varMtx;
void ThreadFunc()
{
std::scoped_lock lck { varMtx };
var++;
std::cout << std::this_thread::get_id() << ": " << var << '\n';
} // <= varMtx
int main()
{
std::thread t1(ThreadFunc);
std::thread t2(ThreadFunc);
t1.join();
t2.join();
return 0;
}
- .
- register . , auto.
- bool.
- . . C++17 , noexcept.
- std::auto_ptr. std::unique_ptr.
- std::random_shuffle. std::shuffle, , . , std::random_shuffle std::rand, .
, C++17 , , , , C++20.
,
PVS-Studio, , . « », . , C++14 . , , .
V798. . C++17 , , ,
std::execution::par , try...catch.
.
PVS-Studio (Windows/Linux) . C++ « » , . PVS-Studio , « » . .
Proof.
- Changes between C++14 and C++17 DIS.
- Youtube. | C++17.
- Youtube. Nicolai Josuttis. ++17. The Language Features. Part 1, Part 2.
- Herb Sutter. Trip report: Summer ISO C++ standards meeting (Oulu).
- Bartlomiej Filipek. C++ 17 Features.
, : Egor Bredikhin.
C++17