Здравствуйте, Videoman, Вы писали:
V>Для примера у нас есть класс представляющий эелемент какого-нибудь json элемента и у него конструкторы. Приходится писать так:
V>V> template <typename BoolType, std::enable_if_t<is_bool_v<BoolType>, int> = 0>
V> Element(BoolType value);
V> template <typename IntType, std::enable_if_t<is_int_v<IntType>, int> = 0>
V> Element(IntType value);
V> template <typename UintType, std::enable_if_t<is_uint_v<UintType>, int> = 0>
V> Element(UintType value);
V> template <typename FloatType, std::enable_if_t<is_float_v<FloatType>, int> = 0>
V> Element(FloatType value);
V> ...
V>
V>С концептами это должно превратиться во что-то типа:
V>V> template <BoolType ValueType>
V> Element(ValueType value);
V> template <IntType ValueType>
V> Element(ValueType value);
V> template <UintType ValueType>
V> Element(ValueType value);
V> template <FloatType ValueType>
V> Element(ValueType value);
V> ...
V>
В этом примере не раскрывается основная фишка концепций в плане overload resolution, а именно частичное упорядочивание требований по степени специфичности (после нормализации). Да, во втором варианте меньше буков, но по структурной сложности одно и то же.
(Как альтернатива, в C++17 можно это же сделать всё внутри одного метода через if constexpr — структурная сложность всё равно подобная, но всё же проще и лаконичнее чем enable_if)
Ниже пример где уже видно частичное упорядочивание, и без дополнительных трюков на голом enable_if не получится:
#include <type_traits>
using namespace std;
template<typename T> concept Arithmetic = is_arithmetic_v<T>;
template<typename T> concept Integral = Arithmetic<T> and is_integral_v<T>;
void with_concept(Arithmetic auto){}
void with_concept(Integral auto){}
template<typename T> enable_if_t<is_arithmetic_v<T>> with_sfinae(T){}
template<typename T> enable_if_t<is_arithmetic_v<T> and is_integral_v<T>> with_sfinae(T){}
int main()
{
with_concept(1);
with_concept(1.0);
// with_sfinae(1); // error: call of overloaded 'with_sfinae(int)' is ambiguous
with_sfinae(1.0);
}
LIVE DEMO
enable_if просто добавляет условие на включение или выключение перегрузки, там нет никакого сравнения специфичности условий. Поэтому в случае
with_sfinae(1), когда оба условия сработали, получается неоднозначность.
До концепций это например решалось через наследование — смотри реализацию
std::advance — там внутри диспатчинг по тэгу итератора, которые
вручную частично упорядоченны наследованием, либо например
жёстко заданным порядкомАвтор: jazzer
Дата: 02.03.10
, либо опять таки можно ввести упорядоченность через
if constexpr.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Ниже пример где уже видно частичное упорядочивание, и без дополнительных трюков на голом enable_if не получится:
EP>EP>#include <type_traits>
EP>using namespace std;
EP>template<typename T> concept Arithmetic = is_arithmetic_v<T>;
EP>template<typename T> concept Integral = Arithmetic<T> and is_integral_v<T>;
EP>void with_concept(Arithmetic auto){}
EP>void with_concept(Integral auto){}
EP>template<typename T> enable_if_t<is_arithmetic_v<T>> with_sfinae(T){}
EP>template<typename T> enable_if_t<is_arithmetic_v<T> and is_integral_v<T>> with_sfinae(T){}
EP>int main()
EP>{
EP> with_concept(1);
EP> with_concept(1.0);
EP> // with_sfinae(1); // error: call of overloaded 'with_sfinae(int)' is ambiguous
EP> with_sfinae(1.0);
EP>}
EP>
EP>LIVE DEMO
EP>enable_if просто добавляет условие на включение или выключение перегрузки, там нет никакого сравнения специфичности условий. Поэтому в случае with_sfinae(1), когда оба условия сработали, получается неоднозначность.
Да, интересное замечание. Действительно полезное свойство.
EP>До концепций это например решалось через наследование — смотри реализацию std::advance — там внутри диспатчинг по тэгу итератора, которые вручную частично упорядоченны наследованием, либо например жёстко заданным порядкомАвтор: jazzer
Дата: 02.03.10
, либо опять таки можно ввести упорядоченность через if constexpr.
Достаточно просто
убрать неоднозначность, правда на практике это бывает сложно сделать:
Здравствуйте, Videoman, Вы писали:
EP>>До концепций это например решалось через наследование — смотри реализацию std::advance — там внутри диспатчинг по тэгу итератора, которые вручную частично упорядоченны наследованием, либо например жёстко заданным порядкомАвтор: jazzer
Дата: 02.03.10
, либо опять таки можно ввести упорядоченность через if constexpr.
V>Достаточно просто убрать неоднозначность, правда на практике это бывает сложно сделать:
Здесь структура намного сложней — условие enable_if каждой перегрузки зависит от всех других, потому что конечные условия должны быть взаимоисключающими. Например попробуй добавить ещё предикаты is_fundamental и is_unsigned.
Если пытаться без концепций, то всё же
if constexpr намного удобнее и лаконичней (конечно там где можно вместо перегрузок использовать один шаблон функции со статическими ветвлениями), так как там можно добавлять
else if не дублируя остальные условия, можно делать вложенные ветвления и т.п.