C++ завтра и послезавтра

Михаил Мальцев

О чём я буду рассказывать

  1. Работа комитета и стандарт C++17
  2. Технические спецификации
  3. Дальнейшее развитие, С++2a
  4. Россия в комитете C++

Комитет WG21

Встречи комитета

Обзор C++17

Нереалистичные ожидания:

Что попало в стандарт (доклад Alisdair Meredith на CppCon 2016):

Список изменений, вошедших в стандарт:

В том числе в стандартную библиотеку:

Изменения в языке

38 предложений (36 фич)

GCCClang
P0217R3 Structured bindings 7 3.9

std::map<std::string, int> get_map();
for (const auto& [k, v] : get_map())
  // ...
    

эквивалентно


for (auto&& __kv : get_map()) {
  const auto& k = std::get<0>(__kv);
  const auto& v = std::get<1>(__kv);
  {
    // ...
  }
}
    

Поддерживается работа со структурами и массивами:


struct foo {
  int i;
  char c;
};

foo f{1, 'a'};
auto& [ii, cc] = f;
    

Рекурсивная версия — в C++Next.

GCCClang
P0091R3 Template argument deduction for class templates 7 -

std::pair p(2, 4.5); // выводится std::pair<int, double>
std::vector v(begin_iter, end_iter); // выводится std::vector<...>
    

Deduction guide:


      template<typename Iter> vector(Iter b, Iter e)
  -> vector<typename iterator_traits<Iter>::value_type>;
    
GCCClang
P0292R2 constexpr if-statements 7 3.9

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
  // ... обработать p
  if constexpr (sizeof...(rs) > 0)
    g(rs...);  // при пустом списке аргументов эта часть
               // не инстанциируется
}
    
GCCClang
P0305R1 Selection statements with initializer 7 3.9
Было:

{
  auto p = m.try_emplace(key, value);
  if (!p.second) {
    FATAL("Element already registered");
  } else {
    process(p.second);
  }
}
          
Стало:

if (auto p = m.try_emplace(key, value); !p.second) {
  FATAL("Element already registered");
} else {
  process(p.second);
}
          
GCCClang
P0145R3 Stricter expression evaluation order 7 4.0

#include <map>
int main() {
  std::map<int, int> m;
  m[0] = m.size();
}
    

Зафиксирован порядок вычисления:

  • a.b, a->b, a->*b, a[b] — слева направо
  • a << b, a >> b — слева направо
  • b = a, b += a, b -= a, … — справа налево

GCCClang
P0188R1 [[fallthrough]] attribute 7 3.9

switch (cond) {
case 0:
case 1:
  foo(1); [[fallthrough]];
case 2:
  bar(2);
  // falls thru
case 3:
  baz(3); // Warning
case 4:
  quax(4);
  break;
  // ...
}
    
g++ test.cc -Wimplicit-fallthrough=3

Стандартизация существующей практики

GCCClang
P0189R1 [[nodiscard]] attribute 7 3.9
P0212R1 [[maybe_unused]] attribute 7 3.9
P0245R1 Hexadecimal floating literals for C++ 3.0 +
P0061R1 __has_include for C++17 5 +

Удалено из стандарта

GCCClang
N4086 Removing trigraphs??! 5 3.5
P0001R1 Remove deprecated register storage class 7 3.8
P0002R1 Remove deprecated bool increment 7 3.8
P0003R5 Removing deprecated dynamic exception specifications 7 4.0

Изменения в стандартной библиотеке

83 предложения

libstdc++libc++
P0024R2 The Parallelism TS Should be Standardized - -

The C++17 Parallel Algorithms Library and Beyond

Параллельные версии алгоритмов и новые алгоритмы, в т.ч.:

  • for_each
  • accumulate и reduce

libstdc++libc++
P0218R1 Adopt the File System TS for C++17 - -

Основные функции:

  • работа с путями
  • перечисление файлов в директории
  • создание директорий и ссылок
  • копирование и перемещение файлов
  • запрос свойств файлов, директорий и ФС
  • запрос и изменение прав доступа

std::variant

libstdc++libc++
P0088R3 Variant: a type-safe union for C++17 7 4.0

std::variant<int, float> v;
v = 12;
std::cout << std::get<int>(v);
          << std::get<0>(v);

// вызовет std::bad_variant_access
std::cout << std::get<float>(v);

v.index();             // 0
std::get_if<float>(v); // nullptr
    
libstdc++libc++
P0220R1 Adopt Library Fundamentals V1 TS Components for C++17 7 4.0

  • optional
  • any
  • string_view
  • полиморфные аллокаторы

std::optional


std::optional<some_class> compute();

auto opt = compute();
if (opt)
  opt->do_something();

// Или:
if (opt.has_value())
  opt.value().do_something();
    

std::any


std::any val{"Hello"s};
auto& str_val = std::any_cast<std::string>(val); // OK

// int_val == nullptr
const int *int_val = std::any_cast<int>(&val);

// исключение std::bad_any_cast
long long_val = std::any_cast<long>(val);
    

std::string_view

Также известен как boost::string_ref.

Упрощенно string_view — это


class string_view {
  const char* m_data;
  size_t m_length;
public:
  // ...
};
    

+ большинство методов std::string

Интеграция std::string_view

libstdc++libc++
P0254R2 Integrating std::string_view and std::string 7 4.0
P0392R0 Adapting string_view by filesystem paths - 4.0
P0403R1 Literal suffixes for basic_string_view - -
P0426R1 Constexpr for std::char_traits - -

Технические спецификации

  • File System
  • Parallelism
  • Library Fundamentals V1
  • Concurrency
  • Transactional memory
  • Modules
  • Coroutines
  • Concepts
  • Ranges
  • Networking

О технических спецификациях

How will all of the above [Networking TS, Concurrency TS, Coroutines] work together?

Jonathan Wakely: They might not. The point of publishing each piece of work as a separate TS is to get implementation experience and user feedback before anything is added to the standard and set in stone. The approach in one TS doesn't necessarily have to agree or interoperate with the approach in another TS. They are experimental ideas and are not intended to form a single, cohesive design.

Модули

Модули, пример


// foo.ixx -> M.ifc, foo.obj
module M;
namespace ns {
export int foo(int x) {
  return 2 + x;
}
}
    

// main.cpp -> main.obj
import M;
int main() {
  ns::foo(5);
  return 0;
}
    

Транзакционная память

N4514 C++ Extensions for Transactional Memory


class account {
public:
  void deposit(int amount);   // пополнить
  void withdraw(int amount);  // списать
  int balance() const;
  // ...
};

bool transfer(account& from, account& to, int amount) {
  if (from.balance() >= amount) {
    from.withdraw(amount);
    to.deposit(amount);
    return true;
  } else {
    return false;
  }
}
    

Добавляются блоки

  • synchronized
  • atomic_noexcept
  • atomic_cancel
  • atomic_commit

и ключевое слово transaction_safe.

Исправленный пример


class account {
public:
  void deposit(int amount) transaction_safe;
  void withdraw(int amount) transaction_safe;
  int balance() const transaction_safe;
  // ...
};

bool transfer(account& from, account& to, int amount) {
  atomic_noexcept {
    if (from.balance() >= amount) {
      from.withdraw(amount);
      to.deposit(amount);
      return true;
    } else {
      return false;
    }
  }
}
    

Сопрограммы


generator hello() {
  for (auto ch : "Hello, ")
    co_yield ch;
  for (auto ch : "world")
    co_yield ch;
}

int main() {
  for (char ch : hello())
    cout << ch;
}
    

Concurrency

P0159R0 C++ Extensions for Concurrency

  • композиция future:
    • when_all
    • when_any
    • then
  • latch и barrier
  • атомарные умные указатели

Networking

N4625 C++ Extensions for Networking

Предложение основано на библиотеке ASIO

  • асинхронная модель
  • буферы
  • сокеты и потоки
  • протоколы IP4, IP6, TCP, UDP

Концепты


template<typename R, typename T>
bool in(R const& range, T const& value);

int main() {
  vector<string> v { ... };
  in(v, 0);
}
    

g++ test.cc

Результат — сообщение об ошибке из 162 строк


template<Range R, typename T>
  requires Equality_comparable<T, Value_type<R>>()
bool in(R const& range, T const& value);

int main() {
  vector<string> v { ... };
  in(v, 0);
}
    

g++ -fconcepts test.cc

In function 'int main()':
  error: cannot call function 'bool in(const R&, const T&)'
     in(v, 0);
            ^
  note: constraints not satisfied
     in(R const& range, T const& value)
     ^
  note: concept Equality_comparable<Value_type<vector<string>>,
  int>() was not satisfied
    

Интервалы

N4622 C++ Extensions for Ranges

Реализация: range-v3

Было:

std::vector<int> v = /* ... */;
std::sort(v.begin(), v.end());
          
Стало:

std::vector<int> v = /* ... */;
std::sort(v);
          

Дальнейшее развитие

  • action — немедленные in-place преобразования
  • view — ленивые преобразования (возвращают новый интервал)

Пример (1² + 2² + … + 10²):


using namespace ranges;
int sum = accumulate(view::ints(1)
                   | view::transform([](int i){return i*i;})
                   | view::take(10), 0);
    

C++Next

Рабочие названия — C++Next, C++2a

Статическая интроспекция (reflection)

  • P0385R1 Static reflection. Rationale, design and evolution.
  • P0194R2 Static reflection

Предоставляет возможность:

  • узнать имена и типы полей структуры
  • получить строковое представление имени типа


float foo = 42.f;
using foo_type = decltype(foo);
using meta_foo = reflexpr(foo);
    

Область применения:

  • сериализация
  • сравнение и хэширование объектов
  • логирование и отладка (pretty print)
  • ORM

В будущем планируется добавить генерацию кода по метаобъектам.

Имеется прототип (на базе Clang).

Операторы сравнения

  • P0221R2 Proposed wording for default comparisons, revision 4
  • P0481R0 Bravely Default
  • P0436R1 An Extensible Approach to Obtaining Selected Operators

Операторы сравнения, пример


struct date {
  int year;
  int month;
  int day;
};

date d1 = /* ... */, d2 = /* ... */;
if (d1 > d2) {
  // ...
} else if (d1 == d2) {
  // ...
}
    

Открытые вопросы

  • opt-in vs opt-out
  • только unordered или unordered+ordered
  • генерировать ли x>=y из !(x<y) или x>y || x==y
  • three-way comparison: int operator<=>

Контракты

P0380R1 A Contract Design


void queue::push(int elem)
  [[ expects: !full() ]]
  [[ ensures: !empty() ]]
{
  // ...
  [[ assert: is_ok() ]];
  // ...
}
    

Оператор "точка"

P0416R1 Operator Dot

Аналогичен операторам * и ->.


template<class X>
class Ref {
public:
  Ref(X& x) :p{&x} {}
  X& operator.() { /* ... */ return *p; }
  // …
private:
  X* p;
};

X val;
Ref ref{val};
ref.foo(); // (r.operator.()).foo()
    

Унифицированный синтаксис вызова

Различия в синтаксисе вызова: x.foo() vs foo(x).

Примеры унификации:

  • операторы: x + y вызывает x.operator+(y) либо operator+(x,y)
  • range-based циклы: вызывается range.begin() либо begin(range)

Унифицированный синтаксис вызова, пример


struct S {
  void foo(int);
};
void bar(const S&);

S s;
.foo(s, 1);  // s.foo(1);
.s.foo(1);   // s.foo(1);
.bar(s);     // bar(s);
.s.bar();    // bar(s);
    

Гетерогенные среды исполнения

  • P0363R0 Towards support for Heterogeneous Devices in C++ (Language aspects)
  • P0362R0 Towards support for Heterogeneous Devices in C++ (Concurrency aspects)

Pattern matching

P0095R1 Pattern Matching and Language Variants


enum command_type { set_score, fire_missile, rotate };
struct command {
  command_type type;
  union {
    std::size_t score;
    double angle;
  } data;
};

switch (cmd.type) {
  case set_score:
    stream << "Set the score to " << cmd.data.score; break;
  case fire_missile:
    stream << "Fire a missile"; break;
  case rotate:
    stream << "Rotate by " << cmd.data.angle << " degrees"; break;
}
    

Pattern matching, пример


lvariant command {
  std::size_t set_score;
  std::monostate fire_missile;
  double rotate;
};

command cmd = command::set_score(10);

inspect (cmd) {
  set_score value =>
    stream << "Set the score to " << value;
  fire_missile m =>
    stream << "Fire a missile";
  rotate degrees =>
    stream << "Rotate by " << degrees << " degrees";
}
    

Pattern matching, открытые вопросы

  • Как задавать альтернативы:
  • Как получать доступ к данным (деконструкция)
  • Синтаксис inspect

РГ 21

stdcpp.ru

Антон Полухин

Нужно больше constexpr

  • P0202R1 Add Constexpr Modifiers to Functions in <algorithm> and <utility> Headers
  • P0415R0 Constexpr for std::complex
  • P0426R1 Constexpr for std::char_traits

Работа с DLL

P0275R0 A Proposal to add Classes and Functions Required for Dynamic Library Load

Основные функции:

  • загрузка DLL (SO)
  • получение адреса символа по имени
  • проверка существования символа

Атрибут [[visible]]

P0276R0 A Proposal to add Attribute [[visible]]

Было:

#if MSVC
# define EXPORT __declspec(dllexport)
#else
# define EXPORT __attribute__((visibility("default")))
#endif

// Public interface
EXPORT bool grph_is_tree(const graph& g);
          
Стало:

[[visible]] bool grph_is_tree(const graph& g);
          

Компиляция:
g++ -shared -fvisibility=hidden …

Мои предложения по улучшению C++

Intrinsic-функции для микробенчмарков

P0412R0 Benchmarking Primitives

Пример


#include <chrono>
#include <iostream>

double perform_computation(int);

void benchmark() {
  using namespace std;
  auto start = chrono::high_resolution_clock::now();
  double answer = perform_computation(42);
  auto delta = chrono::high_resolution_clock::now() - start;
  cout << "The computation took " << delta.count() << " ns";
}
    

Предложение P0412R0

  • touch — помечает glvalue как "модифицированное"
  • keep — помечает значение как "прочитанное"

Исправленный пример


#include <chrono>
#include <iostream>
#include <benchmark>

double perform_computation(int);

void benchmark() {
  using namespace std;
  auto start = chrono::high_resolution_clock::now();
  int value = 42;
  experimental::benchmark::touch(value);
  double answer = perform_computation(value);
  experimental::benchmark::keep(answer);
  auto delta = chrono::high_resolution_clock::now() - start;
  cout << "The computation took "  << delta.count() << " ns";
}
    

P0457R0 String Prefix and Suffix Checking

Предагается добавить методы

  • starts_with
  • ends_with

к шаблонным классам

  • std::basic_string
  • std::basic_string_view

P0458R0 Checking for Existence of an Element in Associative Containers

Было:

if (some_set.find(element) != some_set.end()) {
  // ...
}
        
Стало:

if (some_set.contains(element)) {
  // ...
}
        

Планы на будущее

Включить в стандарт новые функции для форматного вывода a-la fmtlib, Python, Rust:


uint32_t errc;
string_view msg;

// C-style
printf("Error 0x%08x: '%.*s'\n", errc,
       (int)msg.size(), msg.data());

// iostream
cout << "Error 0x" << setw(8) << setfill('0') << ios::hex << errc
     << ": '" << msg << "'\n";

// fmtlib
fmt::print("Error 0x{:08x}: '{}'\n", errc, msg);
    

Спасибо за внимание!