C ++ 17

図2







C ++言語は常に進化しており、静的アナライザーの開発者として、言語のすべての新しい機能をサポートするためにすべての変更を監視することが重要です。 このレビュー記事では、C ++ 17に登場した最も興味深い革新を読者と共有し、それらを例で示したいと思います。



現在、コンパイラ開発者は新しい標準のサポートを積極的に追加しています。 現在サポートされているものを確認するには、リンクをたどってください。





テンプレートパラメーターの畳み込み(折り畳み式)



まず、リストの折り畳みとは何かについてのいくつかの言葉(一般に、折り畳み、縮小、または累積とも呼ばれます)。



畳み込みは、指定された結合関数をリスト内の連続する要素のペアに適用し、結果を返す関数です。 最も単純な例は、畳み込みを使用したリストアイテムの合計です。



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- :



  1. std::execution::seq –
  2. std::execution::par –
  3. 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::reducestd::accumulate, , . , execution policy. :



....
//    vct   
std::reduce(std::execution::par, vct.begin(), vct.end())
....
      
      





std::transform_reduce – , std::reduce.



std::for_each_nstd::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;
}
      
      











, C++17 , , , , C++20.



, PVS-Studio, , . « », . , C++14 . , , . V798. . C++17 , , , std::execution::par , try...catch.



. PVS-Studio (Windows/Linux) . C++ « » , . PVS-Studio , « » . . Proof.





  1. Changes between C++14 and C++17 DIS.
  2. Youtube. | C++17.
  3. Youtube. Nicolai Josuttis. ++17. The Language Features. Part 1, Part 2.
  4. Herb Sutter. Trip report: Summer ISO C++ standards meeting (Oulu).
  5. Bartlomiej Filipek. C++ 17 Features.












, : Egor Bredikhin. C++17






All Articles