Сообщения

Сообщения за октябрь, 2018

Лямбда в развитии (C++11, C++14, C++17)

В этом посте содержится краткое описание того, что касалось лямбда-выражений в при их появлении в С++11 и как они развивались в последующих стандартах. Лямбда-выражения - это упрощенная нотация для определения и использования анонимных объектов-функций вместо определения именованного класса с оператором operator(), последующим созданием объекта этого класса и вызовом его. Основы: Минимальная лямбда содержит 3 части: [] - захват, () - параметры, {} - тело. Последующая лямбда увеличивает на 1 все элементы вектора. #include <algorithm> #include <iostream> #include <vector> int main () { std :: vector < int > v = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 }; auto my_lambda = []( int x ) { return x + 1 ; }; std :: transform ( v . begin (), v . end (), v . begin (), my_lambda ); for ( auto & value : v ) { std :: cout << value << std :: endl ; } return 0 ; } По пов...

"We do not use const rvalue references" Really?

В заголовке цитата из B.Stroustrup "The C++ Programming Language" (Fourth edition)  ( 7.7.2 Rvalue References ) Посмотрим, как это выглядит в действительности. Если заглянуть в драфт последнего стандарта, в раздел 7.3.1 Преобразование lvalue-to-rvalue то мы увидим следующее: A glvalue ( [basic.lval] ) of a non-function, non-array type  T  can be converted to a prvalue . [...] If   T   is a non-class type, the type of the prvalue is the cv-unqualified version of   T .   Otherwise, the type of the prvalue is   T .  Что значит " cv-unqualified "? Опять посмотрим в стандарте : Each type which is a cv-unqualified complete or incomplete object type or is  void  ( [basic.types] ) has three corresponding cv-qualified versions of its type: a  const-qualified  version, a  volatile-qualified  version, and a  const-volatile-qualified  version .  [...] The cv-qualified or cv-unqualified ...

Ещё о перегрузке шаблонных функций (Partial ordering)

[1]  16.2.2 Partial Ordering of Overloaded Function Templates Рассмотрим пример: #include <iostream> template<typename T> int f(T) {     return 1; } template<typename T> int f(T*) {     return 2; } int main() {     std::cout << f<int*>((int*)nullptr); // calls f<T>(T)     std::cout << f<int>((int*)nullptr); // calls f<T>(T*) } Программа имеет вывод : 12. Чтобы понять почему, давайте рассмотрим вызов  f<int*>((int*)nullptr) поподробнее. Синтаксис  f<int*>  указывает, что   мы хотим подставить в первый шаблонный параметр шаблона f() указатель int* не полагаясь на вывод шаблонного аргумента. В этом случае имеется более одного шаблона f(), и поэтому создаётся набор перегрузки, состоящий из двух функций, генерированных из шаблонов: f<int*>(int*) (генерированный из первого шаблона) и f<int*>(int**) (генерированный из втор...

Переменные-шаблоны (variable templates)

Вольный перевод  гл.5.6. Да, мы имеем очень похожие термины для очень разных вещей: Переменная-шаблон ( variable template ) это переменная, которая является шаблоном ("переменная" здесь существительное). Вариативный шаблон (variadic template) - это шаблон с вариативным числом шаблонных параметров ("вариативный" здесь прилагательное). Начиная с С++14, переменные могут быть параметризированы для специфического типа. Такая вещь и называется переменной-шаблоном. Например, мы можем определить значение числа 'пи' не определяя типа этого значения. template < typename T> constexpr T pi{3.1415926535897932385}; Как и для всех шаблонов это объявление не может находиться внутри функции или блока видимости. Чтобы использовать переменную-шаблон, мы должны специфицировать её тип. Например, ниже используются две различные переменные в области видимости, в которой объявлено  pi<>. std::cout << pi <double > << ’\n’ ; std::cout ...

Перегрузка и специализация шаблонных функций.

Попалась на глаза не новая статья на Хабре , в которой автор (достаточно туманно - по моему мнению) разъясняет старую проблему. То, что проблема с бородой, показывает статья Герба Саттера. Ну что же, новое - хорошо забытое старое. Ниже мой вольный перевод статьи Саттера. Почему не специализируются шаблоны функций? В вопросе, поставленном в заголовке статьи одновременно содержится утверждение - когда и почему не стоит специализировать шаблоны. Большая разница: перегрузка против специализации. Краткий обзор того, что мы, возможно забыли. В С++ имеются шаблонные классы ( class templates ) и шаблонные функции( function templates ). Эти две вещи не работают совершенно одинаковыми путями, и наиболее очевидная разница это перегрузка: Старые не-шаблонные классы не перегружаются, как не перегружаются и шаблонные. С другой стороны, старые не-шаблонные функции, имеющие одинаковые имена перегружаются, и поэтому шаблонные функции перегружаются тоже. Всё это совеоршенно естественно. Вот ...

Поддержка декомпозиции при объявлении (structural bindings) для классов

На основе статьи https://blog.tartanllama.xyz/structured-bindings/. С++17 добавил к языку декомпозицию при объявлении , которая позволяет объявлять множество переменных, инициализированных из подобных кортежу объектов: tuple<T1,T2,T3> f(/*...*/) { /*...*/ return {a,b,c}; } auto [x,y,z] = f(); // x имеет тип T1, y имеет тип T2, z имеет тип T3 Это мощный и выразительный механизм языка, имеющий существенное ограничение - декомпозиция работает только с public - членами передаваемой структуры. Поэтому попытка доступа к private - членам класса вызовет соответствующую ошибку при компиляции. Обойти это ограничение можно - необходимо определить специализации std::tuple_size и std::tumpe_element для своего класса и определить шаблонную функцию-член get этого класса (либо дружественную c тем же именем).  Покажем эту технику на примере class Person {     std::string name;     std::size_t age; public:     Person()     { ...