1/38
Метапрограммирование в C++11/14 и C++17
Новые инструменты — новые проблемы
Орлов Роман
St. Petersburg C++ User Group
2/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
3/38
Метапрограммирование
Что это и зачем оно нужно?
Что такое метапрограммирование?
Это реализация алгоритмов, входными/выходными данными которых
являются другие программные сущности (классы, функции, …).
3/38
Метапрограммирование
Что это и зачем оно нужно?
Что такое метапрограммирование?
Это реализация алгоритмов, входными/выходными данными которых
являются другие программные сущности (классы, функции, …).
Метапрограммирование или generic programming?
Generic programming – это способ обобщения реализации (или реализаций)
для разнородных типов данных.
Метапрограммирование – это программирование, подразумевает
описание процесса вычислений.
3/38
Метапрограммирование
Что это и зачем оно нужно?
Что такое метапрограммирование?
Это реализация алгоритмов, входными/выходными данными которых
являются другие программные сущности (классы, функции, …).
Метапрограммирование или generic programming?
Generic programming – это способ обобщения реализации (или реализаций)
для разнородных типов данных.
Метапрограммирование – это программирование, подразумевает
описание процесса вычислений.
Зачем нужно метапрограммирование?
Ускорение выполнения программы, перенос части вычислений на этап
компиляции (zero-overhead abstrac ons).
4/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
5/38
Constexpr if statement
Когда его ещё не придумали
template<class T>
std::enable_if_t<(std::is_floating_point_v<T> || sizeof(T) > 4), T>
div10(T x) {
return x / 10;
}
template<class T>
std::enable_if_t<(std::is_unsigned_v<T> && sizeof(T) <= 4), T>
div10(T x) {
return (0xcccccccdULL * x) >> 0x23;
}
template<class T>
std::enable_if_t<(std::is_signed_v<T> && sizeof(T) <= 4), T>
div10(T x) {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
6/38
Constexpr if statement
Synopsis
if constexpr ([init-statement] condition)
statement
[else
statement]
template<class T>
T div10(T x) {
if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+]
return x / 10;
else if constexpr (std::is_unsigned_v<T>) //[+]
return (0xcccccccdULL * x) >> 0x23;
else {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
}
6/38
Constexpr if statement
Synopsis
if constexpr ([init-statement] condition)
statement
[else
statement]
template<class T>
T div10(T x) {
if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+]
return x / 10;
else if (std::is_unsigned_v<T>) //[-]
return (0xcccccccdULL * x) >> 0x23;
else {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
}
6/38
Constexpr if statement
Synopsis
if constexpr ([init-statement] condition)
statement
[else
statement]
template<class T>
T div10(T x) {
if (std::is_floating_point_v<T> || sizeof(T) > 4) //[-]
return x / 10;
else if (std::is_unsigned_v<T>) //[-]
return (0xcccccccdULL * x) >> 0x23;
else {
auto const t = 0x66666667LL * x;
using U = std::make_unsigned_t<decltype(t)>;
return (t >> 0x22) + ((U)t >> 0x3f);
}
}
7/38
Constexpr if statement
AST для вещественного аргумента
-FunctionDecl used div10 ’double (double)’
|-TemplateArgument type ’double’
|-ParmVarDecl used x ’double’:’double’
‘-CompoundStmt
‘-IfStmt
|-BinaryOperator ’_Bool’ ’||’
| |-<<<...>>>
| ‘-<<<...>>>
|-ReturnStmt
| ‘-BinaryOperator ’double’ ’/’
| |-ImplicitCastExpr ’double’:’double’ <LValueToRValue>
| | ‘-DeclRefExpr lvalue ParmVar ’x’ ’double’:’double’
| ‘-ImplicitCastExpr ’double’ <IntegralToFloating>
| ‘-IntegerLiteral ’int’ 10
‘-<<<NULL>>> // <-- discarded
8/38
Constexpr if statement
AST для беззнакового целочисленного аргумента
-FunctionDecl used div10 ’unsigned int (unsigned int)’
|-TemplateArgument type ’unsigned int’
|-ParmVarDecl used x ’unsigned int’:’unsigned int’
‘-CompoundStmt
‘-IfStmt
|-BinaryOperator ’_Bool’ ’||’
| |-<<<...>>>
| ‘-<<<...>>>
|-NullStmt // <-- discarded
‘-IfStmt
|-ImplicitCastExpr ’_Bool’ <LValueToRValue>
| ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’
|-ReturnStmt
| ‘-ImplicitCastExpr ’unsigned int’:’unsigned int’ <IntegralCast>
| ‘-BinaryOperator ’unsigned long long’ ’>>’
| |-<<<...>>>
| ‘-IntegerLiteral ’int’ 35
‘-<<<NULL>>> // <-- discarded
9/38
Constexpr if statement
AST для целочисленного аргумента
-FunctionDecl used div10 ’int (int)’
|-TemplateArgument type ’int’
|-ParmVarDecl used x ’int’:’int’
‘-CompoundStmt
‘-IfStmt
|-BinaryOperator ’_Bool’ ’||’
| |-<<<...>>>
| ‘-<<<...>>>
|-NullStmt // <-- discarded
‘-IfStmt
|-ImplicitCastExpr ’_Bool’ <LValueToRValue>
| ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’
|-NullStmt // <-- discarded
‘-CompoundStmt
|-DeclStmt
| ‘-VarDecl used t ’const long long’:’const long long’ cinit
| ‘-<<<...>>>
|-DeclStmt
| ‘-TypeAliasDecl U ’std::make_unsigned_t<decltype(t)>’
| ‘-<<<...>>>
‘-ReturnStmt
‘-ImplicitCastExpr ’int’:’int’ <IntegralCast>
‘-<<<...>>>
10/38
Constexpr if statement
и стиль кодирования
auto foo(...) {
if (condition)
return statement1;
return statement2;
}
template<typename T>
auto bar(...) {
if constexpr (condition)
return statement1;
else
return statement2;
}
11/38
Constexpr if statement
Все или ничего
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T const& v) {
if constexpr (std::is_array_v<T>) {
os << ’[’;
auto first = std::begin(v), last = std::end(v);
if (first != last--) {
while (first != last) {
print(os, *first++);
os << ’,’;
}
print(os, *first);
}
os << ’]’;
} else {
os << v;
}
}
11/38
Constexpr if statement
Все или ничего
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T const& v) {
if constexpr (std::is_array_v<T>) {
os << ’[’;
auto first = std::begin(v), last = std::end(v);
if (first != last--) {
while (first != last) {
print(os, *first++);
os << ’,’;
}
print(os, *first);
}
os << ’]’;
} else if constexpr (std::is_enum_v<T>) {
os << ”enum{”;
print(os, static_cast<std::underlying_type_t<T>>(v));
os << ”}”;
} else {
os << v;
}
}
11/38
Constexpr if statement
Все или ничего
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T const& v) {
if constexpr (std::is_array_v<T>) {
os << ’[’;
auto first = std::begin(v), last = std::end(v);
if (first != last--) {
while (first != last) {
print(os, *first++);
os << ’,’;
}
print(os, *first);
}
os << ’]’;
} else if constexpr (std::is_enum_v<T>) {
os << ”enum{”;
print(os, static_cast<std::underlying_type_t<T>>(v));
os << ”}”;
} else if constexpr (...) {
...
} else {
os << v;
}
}
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color {
red,
green,
blue
};
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> [enum{0},enum{1},enum{2}]
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> ???
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> error: call to ’print’ is ambiguous
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print_enum(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, color v) {
return print_enum(os, v);
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> ???
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT, class T>
void print_enum(std::basic_ostream<ChT, TrT>& os, T v,
std::enable_if_t<std::is_enum_v<T>>* = nullptr) {
os << typeid(T).name() << ’=’;
print(os, static_cast<std::underlying_type_t<T>>(v));
}
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, color v) {
return print_enum(os, v);
}
color c[] = {color::red, color::green, color::blue};
print(std::cout, c);
> [5color=0,5color=1,5color=2]
12/38
Constexpr if statement
Проблема расширения
#include ”print.hpp”
enum class color { ... };
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, color v) {
return print_enum(os, v);
}
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, enum1 v) { ... }
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, enum2 v) { ... }
...
template<class ChT, class TrT>
void print(std::basic_ostream<ChT, TrT>& os, enumN v) { ... }
13/38
Constexpr if statement
Ответ РГ21
“Как раньше могли написать плохо, так и теперь можем написать
плохо. Просто по-разному.
”Александр Фокин,
председатель РГ21 C++
14/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
15/38
Метапрограммирование в С++11/14
Выделить частный случай для минимального числа параметров
template<typename T>
void print_list(T t) {
std::cout << t << std::endl;
}
Обработать ”голову” списка параметров, рекурсивно повторить для ”хвоста”
template<typename T, typename U, typename... Tail>
void print_list(T t, U u, Tail... tail) {
std::cout << t << ”, ”; // handle HEAD
return print_list(u, tail...); // handle TAIL
}
16/38
Метапрограммирование в С++11/14
На примере контейнера флагов typed_flags
template<typename... Args>
class typed_flags;
Типы вместо констант и перечислений
class eats_meat;
class eats_grass;
class has_tail;
typed_flags<eats_meat, eats_grass, has_tail> wolf;
Массовая установка флагов
wolf.set<eats_grass>(false);
wolf.set<eats_meat, has_tail>();
Логические операции над множеством флагов
assert( (wolf.all<eats_meat, has_tail>()) );
assert( (wolf.any<eats_meat, has_tail, eats_grass>()) );
assert( (wolf.none<eats_grass>()) );
17/38
Поиск типа в списке в C++11/14
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
17/38
Поиск типа в списке в C++11/14
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
is_same a b | a == b = 1
| otherwise = 0
counter _ [] = 0
counter x (h:t) = is_same x h + counter x t
17/38
Поиск типа в списке в C++11/14
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
is_same a b | a == b = 1
| otherwise = 0
counter _ [] = 0
counter x (h:t) = is_same x h + counter x t
Решение на C++
template<typename T, typename Head=empty, typename... Tail>
struct counter {
static constexpr size_t value = std::is_same_v<T, Head>
+ counter<T, Tail...>::value;
};
template<typename T>
struct counter<T, empty> {
static constexpr size_t value = 0;
};
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
2 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’int’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’float’
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
2 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’int’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’float’
3 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’float’
| |-TemplateArgument pack
18/38
Поиск типа в списке в C++11/14
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
2 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’int’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’float’
3 |-ClassTemplateSpecializationDecl struct counter definition
| |-TemplateArgument type ’void’
| |-TemplateArgument type ’float’
| |-TemplateArgument pack
4 ‘-ClassTemplateSpecializationDecl struct counter definition
|-TemplateArgument type ’void’
|-TemplateArgument type ’struct empty’
|-TemplateArgument pack
19/38
Проверка уникальности списка типов в C++11/14
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
19/38
Проверка уникальности списка типов в C++11/14
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique [] = True
is_unique (h:t) = counter h t == 0 && is_unique t
19/38
Проверка уникальности списка типов в C++11/14
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique [] = True
is_unique (h:t) = counter h t == 0 && is_unique t
Решение на C++
template<typename Head=empty, typename... Tail>
struct is_unique {
static constexpr bool value = counter<Head, Tail...>::value == 0
&& is_unique<Tail...>::value;
};
template<>
struct is_unique<empty> {
static constexpr bool value = true;
};
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’char’
| | ‘-TemplateArgument type ’int’
| |-CXXRecordDecl implicit struct is_unique
| ‘-VarDecl referenced value ’const _Bool’ static inline constexpr
| ‘-BinaryOperator ’_Bool’ ’&&’
| |-BinaryOperator ’_Bool’ ’==’
| | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue>
| | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’char’
| | ‘-TemplateArgument type ’int’
| |-CXXRecordDecl implicit struct is_unique
| ‘-VarDecl referenced value ’const _Bool’ static inline constexpr
| ‘-BinaryOperator ’_Bool’ ’&&’
| |-BinaryOperator ’_Bool’ ’==’
| | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue>
| | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
| | | ‘-ClassTemplateSpecializationDecl struct counter definition
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument pack
| | | | ‘-TemplateArgument type ’int’
20/38
Проверка уникальности списка типов в C++11/14
AST для is_unique<char, char, int>
0 -ClassTemplateDecl is_unique
|-TemplateTypeParmDecl referenced typename Head
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Tail
1 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’char’
| | ‘-TemplateArgument type ’int’
| |-CXXRecordDecl implicit struct is_unique
| ‘-VarDecl referenced value ’const _Bool’ static inline constexpr
| ‘-BinaryOperator ’_Bool’ ’&&’
| |-BinaryOperator ’_Bool’ ’==’
| | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue>
| | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
| | | ‘-ClassTemplateSpecializationDecl struct counter definition
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument type ’char’
| | | |-TemplateArgument pack
| | | | ‘-TemplateArgument type ’int’
2 |-ClassTemplateSpecializationDecl struct is_unique definition
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | ‘-TemplateArgument type ’int’
3 ...
21/38
Поиск номера типа в списке в C++11/14
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
21/38
Поиск номера типа в списке в C++11/14
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
index_of_impl _ _ [] = -1
index_of_impl x n (h:t) | x == h = n
| otherwise = index_of_impl x (n + 1) t
index_of x l = index_of_impl x 0 l
21/38
Поиск номера типа в списке в C++11/14
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
index_of_impl _ _ [] = -1
index_of_impl x n (h:t) | x == h = n
| otherwise = index_of_impl x (n + 1) t
index_of x l = index_of_impl x 0 l
Решение на C++
template<typename T, typename... Args>
struct index_of {
static constexpr size_t value =
index_of_impl<T, 0, Args...>::value;
};
...
22/38
Поиск номера типа в списке в C++11/14
Решение на C++ (продолжение)
...
template<typename T, size_t I, typename H=empty, typename... Args>
struct index_of_impl {
static constexpr size_t value =
index_of_impl<T, I + 1, Args...>::value;
};
template<typename T, size_t I, typename... Args>
struct index_of_impl<T, I, T, Args...> {
static constexpr size_t value = I;
};
template<typename T, size_t I>
struct index_of_impl<T, I, empty> {
static constexpr size_t value = -1;
};
23/38
Поиск номера типа в списке в C++11/14
AST для index_of<void, char, void, int, float>
0 -ClassTemplateDecl index_of_impl
|-TemplateTypeParmDecl referenced typename T
|-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I
|-TemplateTypeParmDecl typename H
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Args
23/38
Поиск номера типа в списке в C++11/14
AST для index_of<void, char, void, int, float>
0 -ClassTemplateDecl index_of_impl
|-TemplateTypeParmDecl referenced typename T
|-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I
|-TemplateTypeParmDecl typename H
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Args
1 |-ClassTemplateSpecializationDecl struct index_of_impl definition
| |-TemplateArgument type ’void’
| |-TemplateArgument integral 0
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’void’
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
| |-CXXRecordDecl implicit struct index_of_impl
23/38
Поиск номера типа в списке в C++11/14
AST для index_of<void, char, void, int, float>
0 -ClassTemplateDecl index_of_impl
|-TemplateTypeParmDecl referenced typename T
|-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I
|-TemplateTypeParmDecl typename H
| ‘-TemplateArgument type ’struct empty’
|-TemplateTypeParmDecl referenced typename ... Args
1 |-ClassTemplateSpecializationDecl struct index_of_impl definition
| |-TemplateArgument type ’void’
| |-TemplateArgument integral 0
| |-TemplateArgument type ’char’
| |-TemplateArgument pack
| | |-TemplateArgument type ’void’
| | |-TemplateArgument type ’int’
| | ‘-TemplateArgument type ’float’
| |-CXXRecordDecl implicit struct index_of_impl
2 ‘-ClassTemplateSpecializationDecl struct index_of_impl definition
|-TemplateArgument type ’void’
|-TemplateArgument integral 1
|-TemplateArgument type ’void’
|-TemplateArgument pack
| |-TemplateArgument type ’int’
| ‘-TemplateArgument type ’float’
|-CXXRecordDecl implicit struct index_of_impl
‘-VarDecl referenced ’const size_t’:’const unsigned int’ static constexpr
‘-SubstNonTypeTemplateParmExpr ’unsigned int’
‘-IntegerLiteral ’unsigned int’ 1
24/38
План
Немного о метапрограммировании
Constexpr if statement
Метапрограммирование в C++11/14
Fold expressions
25/38
Fold expressions
Folding (свертка) – функция высшего порядка, выполняющая преобразование
структуры данных при помощи заданной функции.
le fold (левоассоциативная)
foldl f i [] = i
foldl f i (h:t) = foldl f (f i h) t
foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5
right fold (правоассоциативная)
foldr f i [] = i
foldr f i (h:t) = f h (foldr f i t)
foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0))))
25/38
Fold expressions
Folding (свертка) – функция высшего порядка, выполняющая преобразование
структуры данных при помощи заданной функции.
le fold (левоассоциативная)
foldl f i [] = i
foldl f i (h:t) = foldl f (f i h) t
foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5
right fold (правоассоциативная)
foldr f i [] = i
foldr f i (h:t) = f h (foldr f i t)
foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0))))
auto v = {1, 2, 3, 4, 5};
std::accumulate(std::begin(v), std::end(v), 0, std::plus<int>{});
std::accumulate(std::rbegin(v), std::rend(v), 0, std::plus<int>{});
26/38
Fold expressions в C++17
unary fold
unary le fold
(. . . op pack_expression) ⇒ (((P1 op P2) op . . .) op PN)
unary right fold
(pack_expression op . . .) ⇒ (P1 op (. . . op (PN−1 op PN)))
binary fold
binary le fold
(init op . . . op pack_expression) ⇒ ((((I op P1) op P2) op . . .) op PN)
binary right fold
(pack_expression op . . . op init) ⇒ (P1 op (. . . op (PN−1 op (PN op I))))
op ∈
+   -   *   /   %   ^   &   |   <<   >>
+=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  =
==  !=  <   >   <=  >=  &&  ||  ,   .*   ->*
27/38
GSL and fold expressions (issue #749)
Рекурсия в шаблонном коде тормозит компиляцию.
template<class T>
void pbv(std::vector<T>& v) {}
template<class T, class H, class... Ts>
void pbv(std::vector<T>& v, H&& h, Ts&&... ts) {
v.push_back(h);
pbv(v, std::forward<Ts>(ts)...);
}
Рекурсии можно избежать при помощи
pack expansions
template<class T, class... Ts>
void pbv(std::vector<T>& v, Ts&&... ts) {
int _[] = {(v.push_back(ts), 0)...};
(void)_;
}
fold expressions
template<class T, class... Ts>
void pbv(std::vector<T>& v, Ts&&... ts) {
(..., v.push_back(ts));
}
28/38
Поиск типа в списке в C++17
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
28/38
Поиск типа в списке в C++17
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
counter x l = foldl (r h -> r + is_same h x) 0 l
28/38
Поиск типа в списке в C++17
static_assert(counter<int>::value == 0);
static_assert(counter<int, char, int, float, int>::value == 2);
Решение на Haskell
counter x l = foldl (r h -> r + is_same h x) 0 l
Решение на C++
template<typename T, typename... Args>
struct counter {
static constexpr size_t value =
(0 + ... + std::is_same_v<T, Args>);
};
29/38
Поиск типа в списке в C++17
AST для counter<void, char, int, float>
0 -ClassTemplateDecl counter
|-TemplateTypeParmDecl referenced typename T
|-TemplateTypeParmDecl referenced typename ... Args
|-CXXRecordDecl struct counter definition
| |-CXXRecordDecl implicit struct counter
| ‘-VarDecl value static inline constexpr cinit
| ‘-CXXFoldExpr’<dependent type>’ // New node type
| |-IntegerLiteral ’int’ 0
| ‘-DependentScopeDeclRefExpr ’<dependent type>’ lvalue
1 ‘-ClassTemplateSpecializationDecl struct counter definition
|-TemplateArgument type ’void’
|-TemplateArgument pack
| |-TemplateArgument type ’char’
| |-TemplateArgument type ’int’
| ‘-TemplateArgument type ’float’
|-CXXRecordDecl implicit struct counter
‘-VarDecl referenced value static inline constexpr cinit
‘-ImplicitCastExpr <IntegralCast>
‘-BinaryOperator ’int’ ’+’ // f(T3) + (f(T2) + (0 + f(T1)))
|-BinaryOperator ’int’ ’+’ // f(T2) + (0 + f(T1))
| |-BinaryOperator ’int’ ’+’ // 0 + f(T1)
| | |-IntegerLiteral ’int’ 0
30/38
Поиск типа в списке в C++17
Сравнительная скорость компиляции
10 20 30 40 50 60 70 80 90 100
0
0.5
1
sizeof...(Args)
t,c
C++11/14 Clang
C++11/14 GCC
C++17 Clang
C++17 GCC
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique l = foldl (r h -> r && counter h l == 1) True l
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique l = foldl (r h -> r && counter h l == 1) True l
Решение на C++
template<typename... Args>
struct is_unique {
static constexpr bool value =
(true && ... && (counter<Args, Args...>::value == 1));
};
31/38
Проверка уникальности списка типов в C++17
static_assert( is_unique<>::value);
static_assert(!is_unique<int, char, int>::value);
Решение на Haskell
is_unique l = foldl (r h -> r && counter h l == 1) True l
Решение на C++
template<typename... Args>
struct is_unique {
static constexpr bool value =
(true && ... && (counter<Args, Args...>::value == 1));
};
Решение на C++ (unary le fold)
template<typename... Args>
struct is_unique {
static constexpr bool value =
(... && (counter<Args, Args...>::value == 1));
};
32/38
Проверка уникальности списка типов в C++17
Алгоритмическая проблема
class T1; class T2; class T3; class T4; class T5;
static_assert( is_unique<T1,T2,T3,T4,T5>::value);
Явная рекурсия
1 T1 T2 T3 T4 T5 | N − 1
2 T2 T3 T4 T5 | N − 2
3 T3 T4 T5 | N − 3
4 T4 T5 | N − 4
5 T5 | N − 5
Fold expressions
1 T1 T2 T3 T4 T5 | N
2 T1 T2 T3 T4 T5 | N
3 T1 T2 T3 T4 T5 | N
4 T1 T2 T3 T4 T5 | N
5 T1 T2 T3 T4 T5 | N
33/38
Проверка уникальности списка типов в C++17
Сравнительная скорость компиляции
10 20 30 40 50 60 70 80 90 100
0
20
40
sizeof...(Args)
t,c
C++11/14 Clang
C++11/14 GCC
C++17 Clang
C++17 GCC
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
getn x (t,n) | x == t = n
| otherwise = -1
index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
getn x (t,n) | x == t = n
| otherwise = -1
index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
Решение на C++
template<typename T, typename U, typename... Args>
struct index_of_impl;
template<typename T, typename... Args>
struct index_of {
static constexpr size_t value = index_of_impl<
T, std::index_sequence_for<Args...>, Args...>::value;
};
34/38
Поиск номера типа в списке в C++17
static_assert(index_of<int>::value == -1);
static_assert(index_of<int, char, float, int, void>::value == 2);
Решение на Haskell
getn x (t,n) | x == t = n
| otherwise = -1
index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
Решение на C++
template<typename T, typename U, typename... Args>
struct index_of_impl;
template<typename T, typename... Args>
struct index_of {
static constexpr size_t value = index_of_impl<
T, std::index_sequence_for<Args...>, Args...>::value;
};
template<typename T, size_t... I, typename... Args>
struct index_of_impl<T, std::index_sequence<I...>, Args...> {
static constexpr size_t value = (size_t(-1) OP
... OP (std::is_same_v<T, Args> ? I : size_t(-1)));
};
35/38
Поиск номера типа в списке в C++17
Реализация оператора свертки
template<typename T, typename Fn>
struct fold_arg {
T value;
constexpr fold_arg(T const& v): value{v} {}
constexpr explicit operator T() const { return value; }
constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const {
return Fn{}(value, a.value);
}
};
35/38
Поиск номера типа в списке в C++17
Реализация оператора свертки
template<typename T, typename Fn>
struct fold_arg {
T value;
constexpr fold_arg(T const& v): value{v} {}
constexpr explicit operator T() const { return value; }
constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const {
return Fn{}(value, a.value);
}
};
struct min {
template<typename T>
constexpr T const& operator ()(T const& lhs, T const& rhs) const {
return lhs < rhs ? lhs : rhs;
}
};
using index = fold_arg<size_t, min>;
35/38
Поиск номера типа в списке в C++17
Реализация оператора свертки
template<typename T, typename Fn>
struct fold_arg {
T value;
constexpr fold_arg(T const& v): value{v} {}
constexpr explicit operator T() const { return value; }
constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const {
return Fn{}(value, a.value);
}
};
struct min {
template<typename T>
constexpr T const& operator ()(T const& lhs, T const& rhs) const {
return lhs < rhs ? lhs : rhs;
}
};
using index = fold_arg<size_t, min>;
template<typename T, size_t... I, typename... Args>
struct index_of_impl<T, std::index_sequence<I...>, Args...> {
static constexpr size_t value = static_cast<size_t>((index(-1)
<< ... << index(std::is_same_v<T, Args> ? I : -1)));
};
36/38
Поиск номера типа в списке в C++17
Реализация оператора свертки (easy)
enum index: size_t {};
constexpr index operator << (index lhs, index rhs) {
return lhs < rhs ? lhs : rhs;
}
template<typename T, size_t... I, typename... Args>
struct index_of_impl<T, std::index_sequence<I...>, Args...> {
static constexpr size_t value = (index(-1) <<
... << index(std::is_same_v<T, Args> ? I : -1));
};
37/38
Поиск номера типа в списке в C++17
Сравнительная скорость компиляции
0 10 20 30 40 50 60 70 80 90 100
0.2
0.4
0.6
0.8
1
1.2
1.4
Позиция искомого типа
t,c
C++11/14 Clang
C++11/14 GCC
C++17 Clang
C++17 GCC
38/38
Забудьте все, чему вас учили?
Q: Как вывести последовательность аргументов с разделителем?
A1: Решите задачу рекурсивно
template<typename T>
void print_list(T t) {
std::cout << t << std::endl;
}
template<typename T, typename U, typename... Tail>
void print_list(T t, U u, Tail... tail) {
std::cout << t << ”, ”;
return print_list(u, tail...);
}
A2: Используйте fold expressions, они быстрее!
template<size_t... I, typename... Ts>
void print_list_impl(std::index_sequence<I...>, Ts... args) {
(..., (std::cout << args << (I + 1 < sizeof...(Ts) ? ”, ” : ””)));
}
template<typename... Ts>
void print_list(Ts... args) {
return print_list_impl(std::index_sequence_for<Ts...>{}, args...);
}

Метапрограммирование в C++11/14 и C++17. Новые инструменты - новые проблемы.

  • 1.
    1/38 Метапрограммирование в C++11/14и C++17 Новые инструменты — новые проблемы Орлов Роман St. Petersburg C++ User Group
  • 2.
    2/38 План Немного о метапрограммировании Constexprif statement Метапрограммирование в C++11/14 Fold expressions
  • 3.
    3/38 Метапрограммирование Что это изачем оно нужно? Что такое метапрограммирование? Это реализация алгоритмов, входными/выходными данными которых являются другие программные сущности (классы, функции, …).
  • 4.
    3/38 Метапрограммирование Что это изачем оно нужно? Что такое метапрограммирование? Это реализация алгоритмов, входными/выходными данными которых являются другие программные сущности (классы, функции, …). Метапрограммирование или generic programming? Generic programming – это способ обобщения реализации (или реализаций) для разнородных типов данных. Метапрограммирование – это программирование, подразумевает описание процесса вычислений.
  • 5.
    3/38 Метапрограммирование Что это изачем оно нужно? Что такое метапрограммирование? Это реализация алгоритмов, входными/выходными данными которых являются другие программные сущности (классы, функции, …). Метапрограммирование или generic programming? Generic programming – это способ обобщения реализации (или реализаций) для разнородных типов данных. Метапрограммирование – это программирование, подразумевает описание процесса вычислений. Зачем нужно метапрограммирование? Ускорение выполнения программы, перенос части вычислений на этап компиляции (zero-overhead abstrac ons).
  • 6.
    4/38 План Немного о метапрограммировании Constexprif statement Метапрограммирование в C++11/14 Fold expressions
  • 7.
    5/38 Constexpr if statement Когдаего ещё не придумали template<class T> std::enable_if_t<(std::is_floating_point_v<T> || sizeof(T) > 4), T> div10(T x) { return x / 10; } template<class T> std::enable_if_t<(std::is_unsigned_v<T> && sizeof(T) <= 4), T> div10(T x) { return (0xcccccccdULL * x) >> 0x23; } template<class T> std::enable_if_t<(std::is_signed_v<T> && sizeof(T) <= 4), T> div10(T x) { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); }
  • 8.
    6/38 Constexpr if statement Synopsis ifconstexpr ([init-statement] condition) statement [else statement] template<class T> T div10(T x) { if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+] return x / 10; else if constexpr (std::is_unsigned_v<T>) //[+] return (0xcccccccdULL * x) >> 0x23; else { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); } }
  • 9.
    6/38 Constexpr if statement Synopsis ifconstexpr ([init-statement] condition) statement [else statement] template<class T> T div10(T x) { if constexpr (std::is_floating_point_v<T> || sizeof(T) > 4) //[+] return x / 10; else if (std::is_unsigned_v<T>) //[-] return (0xcccccccdULL * x) >> 0x23; else { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); } }
  • 10.
    6/38 Constexpr if statement Synopsis ifconstexpr ([init-statement] condition) statement [else statement] template<class T> T div10(T x) { if (std::is_floating_point_v<T> || sizeof(T) > 4) //[-] return x / 10; else if (std::is_unsigned_v<T>) //[-] return (0xcccccccdULL * x) >> 0x23; else { auto const t = 0x66666667LL * x; using U = std::make_unsigned_t<decltype(t)>; return (t >> 0x22) + ((U)t >> 0x3f); } }
  • 11.
    7/38 Constexpr if statement ASTдля вещественного аргумента -FunctionDecl used div10 ’double (double)’ |-TemplateArgument type ’double’ |-ParmVarDecl used x ’double’:’double’ ‘-CompoundStmt ‘-IfStmt |-BinaryOperator ’_Bool’ ’||’ | |-<<<...>>> | ‘-<<<...>>> |-ReturnStmt | ‘-BinaryOperator ’double’ ’/’ | |-ImplicitCastExpr ’double’:’double’ <LValueToRValue> | | ‘-DeclRefExpr lvalue ParmVar ’x’ ’double’:’double’ | ‘-ImplicitCastExpr ’double’ <IntegralToFloating> | ‘-IntegerLiteral ’int’ 10 ‘-<<<NULL>>> // <-- discarded
  • 12.
    8/38 Constexpr if statement ASTдля беззнакового целочисленного аргумента -FunctionDecl used div10 ’unsigned int (unsigned int)’ |-TemplateArgument type ’unsigned int’ |-ParmVarDecl used x ’unsigned int’:’unsigned int’ ‘-CompoundStmt ‘-IfStmt |-BinaryOperator ’_Bool’ ’||’ | |-<<<...>>> | ‘-<<<...>>> |-NullStmt // <-- discarded ‘-IfStmt |-ImplicitCastExpr ’_Bool’ <LValueToRValue> | ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’ |-ReturnStmt | ‘-ImplicitCastExpr ’unsigned int’:’unsigned int’ <IntegralCast> | ‘-BinaryOperator ’unsigned long long’ ’>>’ | |-<<<...>>> | ‘-IntegerLiteral ’int’ 35 ‘-<<<NULL>>> // <-- discarded
  • 13.
    9/38 Constexpr if statement ASTдля целочисленного аргумента -FunctionDecl used div10 ’int (int)’ |-TemplateArgument type ’int’ |-ParmVarDecl used x ’int’:’int’ ‘-CompoundStmt ‘-IfStmt |-BinaryOperator ’_Bool’ ’||’ | |-<<<...>>> | ‘-<<<...>>> |-NullStmt // <-- discarded ‘-IfStmt |-ImplicitCastExpr ’_Bool’ <LValueToRValue> | ‘-DeclRefExpr ’const _Bool’ ’is_unsigned_v’ ’const _Bool’ |-NullStmt // <-- discarded ‘-CompoundStmt |-DeclStmt | ‘-VarDecl used t ’const long long’:’const long long’ cinit | ‘-<<<...>>> |-DeclStmt | ‘-TypeAliasDecl U ’std::make_unsigned_t<decltype(t)>’ | ‘-<<<...>>> ‘-ReturnStmt ‘-ImplicitCastExpr ’int’:’int’ <IntegralCast> ‘-<<<...>>>
  • 14.
    10/38 Constexpr if statement истиль кодирования auto foo(...) { if (condition) return statement1; return statement2; } template<typename T> auto bar(...) { if constexpr (condition) return statement1; else return statement2; }
  • 15.
    11/38 Constexpr if statement Всеили ничего template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T const& v) { if constexpr (std::is_array_v<T>) { os << ’[’; auto first = std::begin(v), last = std::end(v); if (first != last--) { while (first != last) { print(os, *first++); os << ’,’; } print(os, *first); } os << ’]’; } else { os << v; } }
  • 16.
    11/38 Constexpr if statement Всеили ничего template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T const& v) { if constexpr (std::is_array_v<T>) { os << ’[’; auto first = std::begin(v), last = std::end(v); if (first != last--) { while (first != last) { print(os, *first++); os << ’,’; } print(os, *first); } os << ’]’; } else if constexpr (std::is_enum_v<T>) { os << ”enum{”; print(os, static_cast<std::underlying_type_t<T>>(v)); os << ”}”; } else { os << v; } }
  • 17.
    11/38 Constexpr if statement Всеили ничего template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T const& v) { if constexpr (std::is_array_v<T>) { os << ’[’; auto first = std::begin(v), last = std::end(v); if (first != last--) { while (first != last) { print(os, *first++); os << ’,’; } print(os, *first); } os << ’]’; } else if constexpr (std::is_enum_v<T>) { os << ”enum{”; print(os, static_cast<std::underlying_type_t<T>>(v)); os << ”}”; } else if constexpr (...) { ... } else { os << v; } }
  • 18.
    12/38 Constexpr if statement Проблемарасширения #include ”print.hpp” enum class color { red, green, blue }; color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > [enum{0},enum{1},enum{2}]
  • 19.
    12/38 Constexpr if statement Проблемарасширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > ???
  • 20.
    12/38 Constexpr if statement Проблемарасширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > error: call to ’print’ is ambiguous
  • 21.
    12/38 Constexpr if statement Проблемарасширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print_enum(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, color v) { return print_enum(os, v); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > ???
  • 22.
    12/38 Constexpr if statement Проблемарасширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT, class T> void print_enum(std::basic_ostream<ChT, TrT>& os, T v, std::enable_if_t<std::is_enum_v<T>>* = nullptr) { os << typeid(T).name() << ’=’; print(os, static_cast<std::underlying_type_t<T>>(v)); } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, color v) { return print_enum(os, v); } color c[] = {color::red, color::green, color::blue}; print(std::cout, c); > [5color=0,5color=1,5color=2]
  • 23.
    12/38 Constexpr if statement Проблемарасширения #include ”print.hpp” enum class color { ... }; template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, color v) { return print_enum(os, v); } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, enum1 v) { ... } template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, enum2 v) { ... } ... template<class ChT, class TrT> void print(std::basic_ostream<ChT, TrT>& os, enumN v) { ... }
  • 24.
    13/38 Constexpr if statement ОтветРГ21 “Как раньше могли написать плохо, так и теперь можем написать плохо. Просто по-разному. ”Александр Фокин, председатель РГ21 C++
  • 25.
    14/38 План Немного о метапрограммировании Constexprif statement Метапрограммирование в C++11/14 Fold expressions
  • 26.
    15/38 Метапрограммирование в С++11/14 Выделитьчастный случай для минимального числа параметров template<typename T> void print_list(T t) { std::cout << t << std::endl; } Обработать ”голову” списка параметров, рекурсивно повторить для ”хвоста” template<typename T, typename U, typename... Tail> void print_list(T t, U u, Tail... tail) { std::cout << t << ”, ”; // handle HEAD return print_list(u, tail...); // handle TAIL }
  • 27.
    16/38 Метапрограммирование в С++11/14 Напримере контейнера флагов typed_flags template<typename... Args> class typed_flags; Типы вместо констант и перечислений class eats_meat; class eats_grass; class has_tail; typed_flags<eats_meat, eats_grass, has_tail> wolf; Массовая установка флагов wolf.set<eats_grass>(false); wolf.set<eats_meat, has_tail>(); Логические операции над множеством флагов assert( (wolf.all<eats_meat, has_tail>()) ); assert( (wolf.any<eats_meat, has_tail, eats_grass>()) ); assert( (wolf.none<eats_grass>()) );
  • 28.
    17/38 Поиск типа всписке в C++11/14 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2);
  • 29.
    17/38 Поиск типа всписке в C++11/14 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell is_same a b | a == b = 1 | otherwise = 0 counter _ [] = 0 counter x (h:t) = is_same x h + counter x t
  • 30.
    17/38 Поиск типа всписке в C++11/14 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell is_same a b | a == b = 1 | otherwise = 0 counter _ [] = 0 counter x (h:t) = is_same x h + counter x t Решение на C++ template<typename T, typename Head=empty, typename... Tail> struct counter { static constexpr size_t value = std::is_same_v<T, Head> + counter<T, Tail...>::value; }; template<typename T> struct counter<T, empty> { static constexpr size_t value = 0; };
  • 31.
    18/38 Поиск типа всписке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail
  • 32.
    18/38 Поиск типа всписке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’
  • 33.
    18/38 Поиск типа всписке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ 2 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’int’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’float’
  • 34.
    18/38 Поиск типа всписке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ 2 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’int’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’float’ 3 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’float’ | |-TemplateArgument pack
  • 35.
    18/38 Поиск типа всписке в C++11/14 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ 2 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’int’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’float’ 3 |-ClassTemplateSpecializationDecl struct counter definition | |-TemplateArgument type ’void’ | |-TemplateArgument type ’float’ | |-TemplateArgument pack 4 ‘-ClassTemplateSpecializationDecl struct counter definition |-TemplateArgument type ’void’ |-TemplateArgument type ’struct empty’ |-TemplateArgument pack
  • 36.
    19/38 Проверка уникальности спискатипов в C++11/14 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value);
  • 37.
    19/38 Проверка уникальности спискатипов в C++11/14 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique [] = True is_unique (h:t) = counter h t == 0 && is_unique t
  • 38.
    19/38 Проверка уникальности спискатипов в C++11/14 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique [] = True is_unique (h:t) = counter h t == 0 && is_unique t Решение на C++ template<typename Head=empty, typename... Tail> struct is_unique { static constexpr bool value = counter<Head, Tail...>::value == 0 && is_unique<Tail...>::value; }; template<> struct is_unique<empty> { static constexpr bool value = true; };
  • 39.
    20/38 Проверка уникальности спискатипов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail
  • 40.
    20/38 Проверка уникальности спискатипов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’char’ | | ‘-TemplateArgument type ’int’ | |-CXXRecordDecl implicit struct is_unique | ‘-VarDecl referenced value ’const _Bool’ static inline constexpr | ‘-BinaryOperator ’_Bool’ ’&&’ | |-BinaryOperator ’_Bool’ ’==’ | | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue> | | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’
  • 41.
    20/38 Проверка уникальности спискатипов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’char’ | | ‘-TemplateArgument type ’int’ | |-CXXRecordDecl implicit struct is_unique | ‘-VarDecl referenced value ’const _Bool’ static inline constexpr | ‘-BinaryOperator ’_Bool’ ’&&’ | |-BinaryOperator ’_Bool’ ’==’ | | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue> | | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’ | | | ‘-ClassTemplateSpecializationDecl struct counter definition | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument pack | | | | ‘-TemplateArgument type ’int’
  • 42.
    20/38 Проверка уникальности спискатипов в C++11/14 AST для is_unique<char, char, int> 0 -ClassTemplateDecl is_unique |-TemplateTypeParmDecl referenced typename Head | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Tail 1 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’char’ | | ‘-TemplateArgument type ’int’ | |-CXXRecordDecl implicit struct is_unique | ‘-VarDecl referenced value ’const _Bool’ static inline constexpr | ‘-BinaryOperator ’_Bool’ ’&&’ | |-BinaryOperator ’_Bool’ ’==’ | | |-ImplicitCastExpr ’size_t’:’unsigned long’ <LValueToRValue> | | | ‘-DeclRefExpr lvalue Var 0x55aa4e871718 ’value’ | | | ‘-ClassTemplateSpecializationDecl struct counter definition | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument type ’char’ | | | |-TemplateArgument pack | | | | ‘-TemplateArgument type ’int’ 2 |-ClassTemplateSpecializationDecl struct is_unique definition | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | ‘-TemplateArgument type ’int’ 3 ...
  • 43.
    21/38 Поиск номера типав списке в C++11/14 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2);
  • 44.
    21/38 Поиск номера типав списке в C++11/14 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell index_of_impl _ _ [] = -1 index_of_impl x n (h:t) | x == h = n | otherwise = index_of_impl x (n + 1) t index_of x l = index_of_impl x 0 l
  • 45.
    21/38 Поиск номера типав списке в C++11/14 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell index_of_impl _ _ [] = -1 index_of_impl x n (h:t) | x == h = n | otherwise = index_of_impl x (n + 1) t index_of x l = index_of_impl x 0 l Решение на C++ template<typename T, typename... Args> struct index_of { static constexpr size_t value = index_of_impl<T, 0, Args...>::value; }; ...
  • 46.
    22/38 Поиск номера типав списке в C++11/14 Решение на C++ (продолжение) ... template<typename T, size_t I, typename H=empty, typename... Args> struct index_of_impl { static constexpr size_t value = index_of_impl<T, I + 1, Args...>::value; }; template<typename T, size_t I, typename... Args> struct index_of_impl<T, I, T, Args...> { static constexpr size_t value = I; }; template<typename T, size_t I> struct index_of_impl<T, I, empty> { static constexpr size_t value = -1; };
  • 47.
    23/38 Поиск номера типав списке в C++11/14 AST для index_of<void, char, void, int, float> 0 -ClassTemplateDecl index_of_impl |-TemplateTypeParmDecl referenced typename T |-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I |-TemplateTypeParmDecl typename H | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Args
  • 48.
    23/38 Поиск номера типав списке в C++11/14 AST для index_of<void, char, void, int, float> 0 -ClassTemplateDecl index_of_impl |-TemplateTypeParmDecl referenced typename T |-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I |-TemplateTypeParmDecl typename H | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Args 1 |-ClassTemplateSpecializationDecl struct index_of_impl definition | |-TemplateArgument type ’void’ | |-TemplateArgument integral 0 | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’void’ | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ | |-CXXRecordDecl implicit struct index_of_impl
  • 49.
    23/38 Поиск номера типав списке в C++11/14 AST для index_of<void, char, void, int, float> 0 -ClassTemplateDecl index_of_impl |-TemplateTypeParmDecl referenced typename T |-NonTypeTemplateParmDecl referenced ’size_t’:’unsigned int’ I |-TemplateTypeParmDecl typename H | ‘-TemplateArgument type ’struct empty’ |-TemplateTypeParmDecl referenced typename ... Args 1 |-ClassTemplateSpecializationDecl struct index_of_impl definition | |-TemplateArgument type ’void’ | |-TemplateArgument integral 0 | |-TemplateArgument type ’char’ | |-TemplateArgument pack | | |-TemplateArgument type ’void’ | | |-TemplateArgument type ’int’ | | ‘-TemplateArgument type ’float’ | |-CXXRecordDecl implicit struct index_of_impl 2 ‘-ClassTemplateSpecializationDecl struct index_of_impl definition |-TemplateArgument type ’void’ |-TemplateArgument integral 1 |-TemplateArgument type ’void’ |-TemplateArgument pack | |-TemplateArgument type ’int’ | ‘-TemplateArgument type ’float’ |-CXXRecordDecl implicit struct index_of_impl ‘-VarDecl referenced ’const size_t’:’const unsigned int’ static constexpr ‘-SubstNonTypeTemplateParmExpr ’unsigned int’ ‘-IntegerLiteral ’unsigned int’ 1
  • 50.
    24/38 План Немного о метапрограммировании Constexprif statement Метапрограммирование в C++11/14 Fold expressions
  • 51.
    25/38 Fold expressions Folding (свертка)– функция высшего порядка, выполняющая преобразование структуры данных при помощи заданной функции. le fold (левоассоциативная) foldl f i [] = i foldl f i (h:t) = foldl f (f i h) t foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5 right fold (правоассоциативная) foldr f i [] = i foldr f i (h:t) = f h (foldr f i t) foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0))))
  • 52.
    25/38 Fold expressions Folding (свертка)– функция высшего порядка, выполняющая преобразование структуры данных при помощи заданной функции. le fold (левоассоциативная) foldl f i [] = i foldl f i (h:t) = foldl f (f i h) t foldl (+) 0 [1,2,3,4,5] = ((((0 + 1) + 2) + 3) + 4) + 5 right fold (правоассоциативная) foldr f i [] = i foldr f i (h:t) = f h (foldr f i t) foldr (+) 0 [1,2,3,4,5] = 1 + (2 + (3 + (4 + (5 + 0)))) auto v = {1, 2, 3, 4, 5}; std::accumulate(std::begin(v), std::end(v), 0, std::plus<int>{}); std::accumulate(std::rbegin(v), std::rend(v), 0, std::plus<int>{});
  • 53.
    26/38 Fold expressions вC++17 unary fold unary le fold (. . . op pack_expression) ⇒ (((P1 op P2) op . . .) op PN) unary right fold (pack_expression op . . .) ⇒ (P1 op (. . . op (PN−1 op PN))) binary fold binary le fold (init op . . . op pack_expression) ⇒ ((((I op P1) op P2) op . . .) op PN) binary right fold (pack_expression op . . . op init) ⇒ (P1 op (. . . op (PN−1 op (PN op I)))) op ∈ +   -   *   /   %   ^   &   |   <<   >> +=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  = ==  !=  <   >   <=  >=  &&  ||  ,   .*   ->*
  • 54.
    27/38 GSL and foldexpressions (issue #749) Рекурсия в шаблонном коде тормозит компиляцию. template<class T> void pbv(std::vector<T>& v) {} template<class T, class H, class... Ts> void pbv(std::vector<T>& v, H&& h, Ts&&... ts) { v.push_back(h); pbv(v, std::forward<Ts>(ts)...); } Рекурсии можно избежать при помощи pack expansions template<class T, class... Ts> void pbv(std::vector<T>& v, Ts&&... ts) { int _[] = {(v.push_back(ts), 0)...}; (void)_; } fold expressions template<class T, class... Ts> void pbv(std::vector<T>& v, Ts&&... ts) { (..., v.push_back(ts)); }
  • 55.
    28/38 Поиск типа всписке в C++17 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2);
  • 56.
    28/38 Поиск типа всписке в C++17 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell counter x l = foldl (r h -> r + is_same h x) 0 l
  • 57.
    28/38 Поиск типа всписке в C++17 static_assert(counter<int>::value == 0); static_assert(counter<int, char, int, float, int>::value == 2); Решение на Haskell counter x l = foldl (r h -> r + is_same h x) 0 l Решение на C++ template<typename T, typename... Args> struct counter { static constexpr size_t value = (0 + ... + std::is_same_v<T, Args>); };
  • 58.
    29/38 Поиск типа всписке в C++17 AST для counter<void, char, int, float> 0 -ClassTemplateDecl counter |-TemplateTypeParmDecl referenced typename T |-TemplateTypeParmDecl referenced typename ... Args |-CXXRecordDecl struct counter definition | |-CXXRecordDecl implicit struct counter | ‘-VarDecl value static inline constexpr cinit | ‘-CXXFoldExpr’<dependent type>’ // New node type | |-IntegerLiteral ’int’ 0 | ‘-DependentScopeDeclRefExpr ’<dependent type>’ lvalue 1 ‘-ClassTemplateSpecializationDecl struct counter definition |-TemplateArgument type ’void’ |-TemplateArgument pack | |-TemplateArgument type ’char’ | |-TemplateArgument type ’int’ | ‘-TemplateArgument type ’float’ |-CXXRecordDecl implicit struct counter ‘-VarDecl referenced value static inline constexpr cinit ‘-ImplicitCastExpr <IntegralCast> ‘-BinaryOperator ’int’ ’+’ // f(T3) + (f(T2) + (0 + f(T1))) |-BinaryOperator ’int’ ’+’ // f(T2) + (0 + f(T1)) | |-BinaryOperator ’int’ ’+’ // 0 + f(T1) | | |-IntegerLiteral ’int’ 0
  • 59.
    30/38 Поиск типа всписке в C++17 Сравнительная скорость компиляции 10 20 30 40 50 60 70 80 90 100 0 0.5 1 sizeof...(Args) t,c C++11/14 Clang C++11/14 GCC C++17 Clang C++17 GCC
  • 60.
    31/38 Проверка уникальности спискатипов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value);
  • 61.
    31/38 Проверка уникальности спискатипов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique l = foldl (r h -> r && counter h l == 1) True l
  • 62.
    31/38 Проверка уникальности спискатипов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique l = foldl (r h -> r && counter h l == 1) True l Решение на C++ template<typename... Args> struct is_unique { static constexpr bool value = (true && ... && (counter<Args, Args...>::value == 1)); };
  • 63.
    31/38 Проверка уникальности спискатипов в C++17 static_assert( is_unique<>::value); static_assert(!is_unique<int, char, int>::value); Решение на Haskell is_unique l = foldl (r h -> r && counter h l == 1) True l Решение на C++ template<typename... Args> struct is_unique { static constexpr bool value = (true && ... && (counter<Args, Args...>::value == 1)); }; Решение на C++ (unary le fold) template<typename... Args> struct is_unique { static constexpr bool value = (... && (counter<Args, Args...>::value == 1)); };
  • 64.
    32/38 Проверка уникальности спискатипов в C++17 Алгоритмическая проблема class T1; class T2; class T3; class T4; class T5; static_assert( is_unique<T1,T2,T3,T4,T5>::value); Явная рекурсия 1 T1 T2 T3 T4 T5 | N − 1 2 T2 T3 T4 T5 | N − 2 3 T3 T4 T5 | N − 3 4 T4 T5 | N − 4 5 T5 | N − 5 Fold expressions 1 T1 T2 T3 T4 T5 | N 2 T1 T2 T3 T4 T5 | N 3 T1 T2 T3 T4 T5 | N 4 T1 T2 T3 T4 T5 | N 5 T1 T2 T3 T4 T5 | N
  • 65.
    33/38 Проверка уникальности спискатипов в C++17 Сравнительная скорость компиляции 10 20 30 40 50 60 70 80 90 100 0 20 40 sizeof...(Args) t,c C++11/14 Clang C++11/14 GCC C++17 Clang C++17 GCC
  • 66.
    34/38 Поиск номера типав списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2);
  • 67.
    34/38 Поиск номера типав списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell getn x (t,n) | x == t = n | otherwise = -1 index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..])
  • 68.
    34/38 Поиск номера типав списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell getn x (t,n) | x == t = n | otherwise = -1 index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..]) Решение на C++ template<typename T, typename U, typename... Args> struct index_of_impl; template<typename T, typename... Args> struct index_of { static constexpr size_t value = index_of_impl< T, std::index_sequence_for<Args...>, Args...>::value; };
  • 69.
    34/38 Поиск номера типав списке в C++17 static_assert(index_of<int>::value == -1); static_assert(index_of<int, char, float, int, void>::value == 2); Решение на Haskell getn x (t,n) | x == t = n | otherwise = -1 index_of x l = foldl (r h -> max r (getn x h)) (-1) (zip l [0..]) Решение на C++ template<typename T, typename U, typename... Args> struct index_of_impl; template<typename T, typename... Args> struct index_of { static constexpr size_t value = index_of_impl< T, std::index_sequence_for<Args...>, Args...>::value; }; template<typename T, size_t... I, typename... Args> struct index_of_impl<T, std::index_sequence<I...>, Args...> { static constexpr size_t value = (size_t(-1) OP ... OP (std::is_same_v<T, Args> ? I : size_t(-1))); };
  • 70.
    35/38 Поиск номера типав списке в C++17 Реализация оператора свертки template<typename T, typename Fn> struct fold_arg { T value; constexpr fold_arg(T const& v): value{v} {} constexpr explicit operator T() const { return value; } constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const { return Fn{}(value, a.value); } };
  • 71.
    35/38 Поиск номера типав списке в C++17 Реализация оператора свертки template<typename T, typename Fn> struct fold_arg { T value; constexpr fold_arg(T const& v): value{v} {} constexpr explicit operator T() const { return value; } constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const { return Fn{}(value, a.value); } }; struct min { template<typename T> constexpr T const& operator ()(T const& lhs, T const& rhs) const { return lhs < rhs ? lhs : rhs; } }; using index = fold_arg<size_t, min>;
  • 72.
    35/38 Поиск номера типав списке в C++17 Реализация оператора свертки template<typename T, typename Fn> struct fold_arg { T value; constexpr fold_arg(T const& v): value{v} {} constexpr explicit operator T() const { return value; } constexpr fold_arg<T,Fn> operator <<(fold_arg<T,Fn> const& a) const { return Fn{}(value, a.value); } }; struct min { template<typename T> constexpr T const& operator ()(T const& lhs, T const& rhs) const { return lhs < rhs ? lhs : rhs; } }; using index = fold_arg<size_t, min>; template<typename T, size_t... I, typename... Args> struct index_of_impl<T, std::index_sequence<I...>, Args...> { static constexpr size_t value = static_cast<size_t>((index(-1) << ... << index(std::is_same_v<T, Args> ? I : -1))); };
  • 73.
    36/38 Поиск номера типав списке в C++17 Реализация оператора свертки (easy) enum index: size_t {}; constexpr index operator << (index lhs, index rhs) { return lhs < rhs ? lhs : rhs; } template<typename T, size_t... I, typename... Args> struct index_of_impl<T, std::index_sequence<I...>, Args...> { static constexpr size_t value = (index(-1) << ... << index(std::is_same_v<T, Args> ? I : -1)); };
  • 74.
    37/38 Поиск номера типав списке в C++17 Сравнительная скорость компиляции 0 10 20 30 40 50 60 70 80 90 100 0.2 0.4 0.6 0.8 1 1.2 1.4 Позиция искомого типа t,c C++11/14 Clang C++11/14 GCC C++17 Clang C++17 GCC
  • 75.
    38/38 Забудьте все, чемувас учили? Q: Как вывести последовательность аргументов с разделителем? A1: Решите задачу рекурсивно template<typename T> void print_list(T t) { std::cout << t << std::endl; } template<typename T, typename U, typename... Tail> void print_list(T t, U u, Tail... tail) { std::cout << t << ”, ”; return print_list(u, tail...); } A2: Используйте fold expressions, они быстрее! template<size_t... I, typename... Ts> void print_list_impl(std::index_sequence<I...>, Ts... args) { (..., (std::cout << args << (I + 1 < sizeof...(Ts) ? ”, ” : ””))); } template<typename... Ts> void print_list(Ts... args) { return print_list_impl(std::index_sequence_for<Ts...>{}, args...); }