Post on 15-Apr-2017
От Concepts к Concepts Lite
Александр Фокин Старший разработчик Поиска
План
● Мотивация
● Немного истории
● Concepts Lite
Мотивация
4
Сообщения об ошибках
5
1>include\algorithm(3157): error C2784: 'unknown-‐type std::operator -‐(std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)' : 1> could not deduce template argument for 'std::move_iterator<_RanIt> &' from 1> 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>' 1> include\xutility(1969) : see declaration of 'std::operator -‐' 1> include\algorithm(3164) : see reference to function template instantiation 1> 'void std::sort<_RanIt,std::less<void>>(_RanIt,_RanIt,_Pr)' being compiled 1> with 1> [ 1> _RanIt=std::_List_iterator<std::_List_val<std::_List_simple_types<int>>> 1> , _Pr=std::less<void> 1> ] 1> main.cpp(8) : see reference to function template instantiation 1> 'void std::sort<std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>>(_RanIt,_RanIt)' being compiled 1> with 1> [ 1> _RanIt=std::_List_iterator<std::_List_val<std::_List_simple_types<int>>> 1> ] 1> include\xutility(1969) : see declaration of 'std::operator -‐' 1> include\xutility(1969) : see declaration of 'std::operator -‐' 1> include\xutility(1969) : see declaration of 'std::operator -‐' 1> include\xutility(1969) : see declaration of 'std::operator -‐' 1>include\algorithm(3157): error C2784: 'unknown-‐type std::operator -‐(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : 1> could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' 1> from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>' 1> include\xutility(1130) : see declaration of 'std::operator -‐' 1> include\xutility(1130) : see declaration of 'std::operator -‐' 1> include\xutility(1130) : see declaration of 'std::operator -‐' 1> include\xutility(1130) : see declaration of 'std::operator -‐' 1> include\xutility(1130) : see declaration of 'std::operator -‐' 1>include\algorithm(3157): error C2784: 'unknown-‐type std::operator -‐(const std::_Revranit<_RanIt,_Base> &,const std::_Revranit<_RanIt2,_Base2> &)' : 1> could not deduce template argument for 'const std::_Revranit<_RanIt,_Base> &' 1> from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>' 1> include\xutility(937) : see declaration of 'std::operator -‐' 1> include\xutility(937) : see declaration of 'std::operator -‐' 1> include\xutility(937) : see declaration of 'std::operator -‐' 1> include\xutility(937) : see declaration of 'std::operator -‐' 1> xutility(937) : see declaration of 'std::operator -‐' 1>include\algorithm(3157): error C2676: binary '-‐' : 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>' does not define this 1> operator or a conversion to a type acceptable to the predefined operator 1>include\algorithm(3157): error C2780: 'void std::_Sort(_RanIt,_RanIt,_Diff,_Pr)' : expects 4 arguments -‐ 3 provided 1> include\algorithm(3121) : see declaration of 'std::_Sort'
std::list<int> list; std::sort(list.begin(), list.end());
Cообщения об ошибках
6
Что видит опытный разработчик Что видит новичок
main.cpp(8): list<int>::iterator is not a RandomAccessIterator
水無月の虚空に涼し時鳥 木をつみて夜の明やすき小窓かな 一重づゝ一重つゝ散れ八重櫻 名月の出るやゆらめく花薄 ちる花にもつるゝ鳥の翼かな 麥蒔やたばねあげたる桑の枝 松杉や枯野の中の不動堂 すゝしさや神と佛の隣同士 御佛に尻むけ居れば月涼し 見下せば月にすゞしや四千軒 月涼し蛙の聲のわきあがる すゞしさや瀧ほとばしる家のあひ 春風に尾をひろげたる孔雀かな 柿くへば鐘が鳴るなり法隆寺 稻の花道灌山の日和かな
O_O
Перегрузка функций
7
template<class Iterator, class Difference> typename std::enable_if< is_input_iterator<Iterator> && !is_bidirectional_iterator<Iterator> >::type advance(Iterator &i, Difference n) { while(n-‐-‐) ++i; } template<class Iterator, class Difference> typename std::enable_if< is_bidirectional_iterator<Iterator> && !is_random_access_iterator<Iterator> >::type advance(Iterator &i, Difference n) { if (n > 0) while (n-‐-‐) ++i; if (n < 0) while (n++) -‐-‐i; } template<class Iterator, class Difference> typename std::enable_if<is_random_access_iterator<Iterator>>::type advance(Iterator &i, Difference n) { i += n; }
Перегрузка функций
8
Что видит опытный разработчик Что видит новичок
void advance(InputIterator &i, Difference n) { while(n-‐-‐) ++i; }
template<水無月の虚空に涼し時鳥> 本末転倒 advance(Iterator &i, Difference n) { while(n-‐-‐) ++i; }
O_O
Концепты
9
Динамический полиморфизм: ● Интерфейсы
Статический полиморфизм: ● Концепты
interface List<T> extends Collection<T> {...} static <T> void sort(List<T> list);
template<class T> concept bool Sortable() {...} template<Sortable Container> void sort(Container& c);
Java C++
Немного истории
10
Концепты в С++
11
1995 2000 2005 2010 2015 2020
1994: STL принята в стандарт С++.
Pre-Standard С++98 С++03 С++11 С++14 С++17+
1996: Благодаря распространению STL шаблоны начинают активно использовать.
2002: Шаблонами из boost пугают детей. Становятся видны проблемы текущей модели шаблонов. Разрабатываются «ограничения на шаблоны» — концепты.
2005: Концепты решено включить в C++0x.
2009: Концепты решено исключить из C++0x.
2013: Concepts Lite — ограничения на шаблоны и ничего больше.
2017: Concepts Lite в C++17
12
Какими были концепты в С++0x?
Проверка аргументов
13 13
template<ForwardIterator I, class V> requires Assignable<typename I::value_type, V> void fill(I first, I last, const V& v); fill(0, 9, 9.9); // Error: int is not a ForwardIterator fill(&v[0], &v[9], 9.9); // OK
Проверка реализации
14
template<ForwardIterator I, class V> requires Assignable<typename I::value_type, V> void fill(I first, I last, const V &v) { while (first != last) { *first = v; first = first + 1; // Error: operator+ not defined // for ForwardIterator } }
Проверка реализации
15
concept Storable<class T> { void store_value(T); }; template<Storable T> void store(const T& value) { store_value(value); // OK }
Проверка реализации
16
concept Storable<class T> { void store_value(T); }; template<Storable T> void store(const T& value) { store_value(value); // OK #ifdef _DEBUG std::cerr << “STORE ” << value << std::endl; #endif }
Проверка реализации
17
concept Storable<class T> { void store_value(T); }; template<Storable T> void store(const T& value) { store_value(value); // OK #ifdef _DEBUG std::cerr << “STORE ” << value << std::endl; // Error: operator<< not defined #endif // for Storable }
Расширяемость через concept_map
18
concept RandomAccessIterator<class T> { /* ... */ }; template<RandomAccessIterator I> typename I::value_type range_max(I first, I last); int array[N] = { /* ... */ }; std::cout << range_max(&array[0], &array[N]); // Error: int* has // no nested value_type
Расширяемость через concept_map
19
concept RandomAccessIterator<class T> { /* ... */ }; template<class T> concept_map RandomAccessIterator<T *> { typedef T value_type; }; template<RandomAccessIterator I> typename I::value_type range_max(I first, I last); int array[N] = { /* ... */ }; std::cout << range_max(&array[0], &array[N]); // OK
Аксиомы
20
concept TotalOrdering<class Op, class T> { bool operator()(Op, T, T); axiom Antisymmetry(Op op, T x, T y) { if (op(x, y) && op(y, x)) x <=> y; } axiom Transitivity(Op op, T x, T y, T z) { if (op(x, y) && op(y, z)) op(x, z); } axiom Totality(Op op, T x, T y) { op(x, y) || op(y, x); } }
Проблемы
21
● «Слишком много всего»
● Большое количество спорных моментов
● Очень много стандартных концептов
Concepts Lite
22
Concepts Lite
23
● Проверка аргументов в местах вызова
● Проверка реализации
● Расширяемость через concept_map
● Аксиомы для определения семантики
Ограничения на шаблоны
● Поддержка обобщенного программирования
● Первый шаг на пути к полноценным концептам
Синтаксис
25
template<Sortable C> void sort(C& container);
error: no matching function call to ‘sort(list<int>&)’ note: candidate is ‘sort(C& container)’ note: where C = list<int> note: template constraints not satisfied note: ‘C’ is not a/an ‘Sortable’ type
list<int> l; sort(l);
Концепты
26
template<class T> concept bool Sortable = PermutableСontainer<T> && TotallyOrdered<ValueType<T>>; template<class T> concept bool Integral() { return std::is_integral<T>(); }
Обычные constexpr шаблонные переменные или функции!
Ограничения на классы
27
template<Object T, Allocator A> class vector; /* Equivalently: */ template<class T, class A> requires Object<T> && Allocator<A> class vector;
Ограничения на несколько типов
28
template<Sequence S, Equality_comparable<Value_type<S>> T> Iterator_type<S> find(S&& sequence, const T& value); /* Equivalently: */ template<class S, class T> requires Sequence<S> && Equality_comparable<T, Value_type<S>> Iterator_type<S> find(S&& sequence, const T& value); /* Or: */ template<Sequence S, class T> requires Equality_comparable<T, Value_type<S>> Iterator_type<S> find(S&& sequence, const T& value);
Ограничения на методы
29
template<Object T, Allocator A> class vector { public: requires Copyable<T> vector(const vector& x); requires Movable<T> void push_back(T&& x); };
Перегрузка функций
30
template<InputIterator I> void advance(I& i, DifferenceType<I> n) { while (n-‐-‐) ++i; } template<BidirectionalIterator I> void advance(I& i, DifferenceType<I> n) { if (n > 0) while (n-‐-‐) ++i; if (n < 0) while (n++) -‐-‐i; } template<RandomAccessIterator I> void advance(I& i, DifferenceType<I> n) { i += n; }
Перегрузка функций
31
istream_iterator<int> iter(cin); advance(iter, 1); // Input overload vector<int>::iterator first = v.begin(); advance(first, 1); // Random access overload
Компилятор выбирает наиболее «специализированную» перегрузку путем сравнения множеств ограничений на типы.
Специализация шаблонов
32
template<class T> struct numeric_traits; template<Integral T> struct numeric_traits<T> : integral_traits<T> { /*...*/ }; template<FloatingPoint T> struct numeric_traits<T> : floating_point_traits<T> { /*...*/ };
Обобщенные лямбда-функции
33
vector<string> v = { /*...*/ }; find_if(v, [](const auto& x) { return x == "hello"; });
template<class T> concept bool String = /*...*/;
[](const String& x) { return x == "hello"; } []<String T>(const T& x) { return x == "hello"; } [] String{T} (const T& x) { return x == "hello"; }
Обобщенные лямбда-функции
34
template<class I, class P> concept bool Input_query() = Input_iterator<I> && Predicate<P, Value_type<I>>;
template<class I, class P> requires Input_query<I, P> I find_if(I first, I last, P pred); /* As a lambda: */ auto find_if = [] Input_query{I, P} (I first, I last, P pred) {}; /* As a function with {} syntax: */ Input_query{I, P} I find_if(I first, I last, P pred);
Замена auto
35
Integer gcd(Integer a, Integer b) { /* ... */ } /* Equivalently: */ template<Integer T> T gcd(T a, T b);
Real y = f(x); /* Equivalently: */ auto y = f(x); static_assert(Real<decltype(y)>(), "");
Выражение requires
36
template<class T> concept bool Equality_comparable = requires (T a, T b) { {a == b} -‐> bool; {a != b} -‐> bool; }; template<class I> concept bool Readable = requires (I i) { typename Value_type<I>; {*i} -‐> const Value_type<I>&; };
Концепты
37
● Возвращают bool
● Не имеют параметров
● Не имеют ограничений
● Объявление должно быть определением
● Определение не должно быть рекурсивным
● Перегрузки разрешены только по количеству параметров
Декомпозиция концептов
38
template<class T> requires Equality_comparable<T> bool distinct(T a, T b) { return a != b; } /* Decomposed: */ template<class T> requires __is_valid_expr(declval<T>() == declval<T>()) && __is_convertible_to(decltype(declval<T>() == declval<T>()), bool) && __is_valid_expr(declval<T>() != declval<T>()) && __is_convertible_to(decltype(declval<T>() != declval<T>()), bool) bool distinct(T a, T b)
Декомпозиция концептов
39
Атомарные ограничения
template<class T> requires Equality_comparable<T> bool distinct(T a, T b) { return a != b; } /* Decomposed: */ template<class T> requires __is_valid_expr(declval<T>() == declval<T>()) && __is_convertible_to(decltype(declval<T>() == declval<T>()), bool) && __is_valid_expr(declval<T>() != declval<T>()) && __is_convertible_to(decltype(declval<T>() != declval<T>()), bool) bool distinct(T a, T b)
Атомарные ограничения
40
Все, что не является выражением с операторами && и ||:
● is_integral<T>::value
● !is_void<T>::value
● __is_valid_expr(declval<T>() == declval<T>())
Вызовы концептов не являются атомарными.
Упорядочивание концептов
41
template<class T> concept bool Totally_ordered = Weakly_ordered<T> && Equality_comparable<T>;
Totally_ordered
Equality_comparable
Когда?
42
● В июле 2015 Concepts Lite TS было одобрено комитетом по стандартизации
● С большой вероятностью будет включено в стандарт C++17
Хотите узнать больше?
43
● N3701 на https://isocpp.org
● Лекции Andrew Sutton, одного из авторов предложения по включению Concepts Lite в стандарт
● Бранч GCC: http://concepts.axiomatics.org/~ans/
● Реализация части STL c концептами: https://github.com/asutton/origin
Спасибо!
44
Контакты
elric@yandex-team.ru
+7 915 3705385
retgone
retgone
Александр Фокин
Старший разработчик Поиска
47
Addendum
Причины
std::vector<int&> v;
Причины
49
1>include\xmemory0(516): error C2528: 'pointer' : pointer to reference is illegal 1> include\type_traits(572) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled 1> with 1> [ 1> _Ty=int & 1> ] 1> include\vector(650) : see reference to class template instantiation 'std::is_empty<_Alloc>' being compiled 1> with 1> [ 1> _Alloc=std::allocator<int &> 1> ] 1> main.cpp(6) : see reference to class template instantiation 'std::vector<int &,std::allocator<_Ty>>' being compiled 1> with 1> [ 1> _Ty=int & 1> ] 1>include\xmemory0(517): error C2528: 'const_pointer' : pointer to reference is illegal 1>include\xmemory0(548): error C2535: 'int &(*std::allocator<_Ty>::address(int &) throw() const)' : member function already defined or declared 1> with 1> [ 1> _Ty=int & 1> ] 1> include\xmemory0(542) : see declaration of 'std::allocator<_Ty>::address' 1> with 1> [ 1> _Ty=int & 1> ] 1>include\xmemory0(586): error C2528: '_Ptr' : pointer to reference is illegal 1>include\xmemory0(591): error C2528: '_Ptr' : pointer to reference is illegal 1>include\xmemory0(683): error C2528: 'pointer' : pointer to reference is illegal 1> include\xmemory0(755) : see reference to class template instantiation 'std::allocator_traits<_Alloc>' being compiled 1> with 1> [ 1> _Alloc=std::allocator<int &> 1> ] 1> include\vector(443) : see reference to class template instantiation 'std::_Wrap_alloc<std::allocator<_Ty>>' being compiled 1> with 1> [ 1> _Ty=int & 1> ] 1> include\vector(579) : see reference to class template instantiation 'std::_Vec_base_types<_Ty,_Alloc>' being compiled 1> with 1> [ 1> _Ty=int & 1> , _Alloc=std::allocator<int &> 1> ] 1> include\vector(652) : see reference to class template instantiation 'std::_Vector_alloc<false,std::_Vec_base_types<_Ty,_Alloc>>' being compiled 1> with 1> [ 1> _Ty=int & 1> , _Alloc=std::allocator<int &> 1> ] 1>include\xmemory0(684): error C2528: 'const_pointer' : pointer to reference is illegal 1>include\xmemory0(795): error C2535: 'int &(*std::_Wrap_alloc<std::allocator<_Ty>>::address(int &) const)' : member function already defined or declared 1> with 1> [ 1> _Ty=int & 1> ] 1> include\xmemory0(789) : see declaration of 'std::_Wrap_alloc<std::allocator<_Ty>>::address' 1> with 1> [ 1> _Ty=int & 1> ] 1>include\xmemory0(861): error C2528: '_Ptr' : pointer to reference is illegal 1>include\xmemory0(105): error C2528: 'abstract declarator' : pointer to reference is illegal 1> include\vector(449) : see reference to class template instantiation 'std::_Is_simple_alloc<std::_Wrap_alloc<std::allocator<_Ty>>>' being compiled 1> with 1> [ 1> _Ty=int & 1> ] 1>include\xmemory0(107): error C2528: 'abstract declarator' : pointer to reference is illegal 1>include\xmemory0(122): error C2528: 'pointer' : pointer to reference is illegal 1> include\vector(469) : see reference to class template instantiation 'std::_Simple_types<int &>' being compiled 1> include\vector(580) : see reference to class template instantiation 'std::_Vector_val<std::_Simple_types<int &>>' being compiled 1>include\xmemory0(123): error C2528: 'const_pointer' : pointer to reference is illegal 1>include\vector(797): error C2528: '_Pval' : pointer to reference is illegal 1>include\vector(1243): error C2535: 'void std::vector<int &,std::allocator<_Ty>>::push_back(int &)' : member function already defined or declared 1> with 1> [ 1> _Ty=int & 1> ] 1> include\vector(864) : see declaration of 'std::vector<int &,std::allocator<_Ty>>::push_back' 1> with 1> [ 1> _Ty=int & 1> ] 1>include\vector(1327): error C2535: 'std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int &>>> std::vector<int &,std::allocator<_Ty>>::insert(std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int &>>>,_Ty)' : member function already defined or declared 1> with 1> [ 1> _Ty=int & 1> ] 1> include\vector(887) : see declaration of 'std::vector<int &,std::allocator<_Ty>>::insert' 1> with 1> [ 1> _Ty=int & 1> ] 1>include\vector(1581): error C2528: '_Ptr' : pointer to reference is illegal 1>include\vector(1745): error C2528: '_Pval' : pointer to reference is illegal
Шаблоны в С++
Методология программирования, основанная на разделении структур данных и алгоритмов через использование абстрактных описаний требований.
Перегруженные функции
51
vector<double> v{ /* ... */ }; Number n = accumulate(v.begin(), v.end(), operator*); /* Equivalently: */ Number n = accumulate( v.begin(), v.end(), [](auto a, auto b) { return operator*(a, b)} );