Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.
Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
using rval = T;
template<class T>
void f(rval<T>&&)
{
std::cout << "rvalue_reference\n";
}
template<class T>
void f(T&)
{
std::cout << "lvalue_reference\n";
}
int main()
{
int i = 0;
f(0);
f(i);
}
Здравствуйте, night beast, Вы писали:
NB>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
NB>
Давно этим пользуюсь. Код гораздо чище получается и его легче менять. Странно что вас это удивляет, ведь даже в вашем примере, сам std::enable_if_t — это и есть, такой же, шаблонный using от простого std::enable_if.
Здравствуйте, Videoman, Вы писали:
V>Давно этим пользуюсь. Код гораздо чище получается и его легче менять. Странно что вас это удивляет, ведь даже в вашем примере, сам std::enable_if_t — это и есть, такой же, шаблонный using от простого std::enable_if.
да даже не задумывался о такой возможности
привык все через enable_if решать.
NB>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
NB>
Здравствуйте, _hum_, Вы писали:
NB>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
NB>>
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, _hum_, Вы писали:
NB>>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
NB>>>
__>>а почему название для типа rval? как-то очень сбивает с толку (возникают ассоциации с r-value), ведь на деле это просто non_ref_type.
NB>ассоциация правильная. NB>при perfect forwarding'е у l-value будет тип T&
и? вы, случаем, не путаете l-/r-/pr- values (имеющие все нессылочные типы non_ref_type) со значениями ссылок на них (имеющие ссылочные типы наподобие non_ref_type&, non_ref_type&&)?
Здравствуйте, _hum_, Вы писали:
NB>>>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>>>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
NB>>>>
__>>>а почему название для типа rval? как-то очень сбивает с толку (возникают ассоциации с r-value), ведь на деле это просто non_ref_type.
NB>>ассоциация правильная. NB>>при perfect forwarding'е у l-value будет тип T&
__>и? вы, случаем, не путаете l-/r-/pr- values (имеющие все нессылочные типы non_ref_type) со значениями ссылок на них (имеющие ссылочные типы наподобие non_ref_type&, non_ref_type&&)?
не расшифровал.
using был сделан для отделения rval от lval при perfect forwarding'е и он с этим справляется. чего еще нужно?
заменить название на non_ref_type -- значит ухудшить читабельность.
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, _hum_, Вы писали:
NB>>>>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>>>>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
NB>>>>>
__>>>>а почему название для типа rval? как-то очень сбивает с толку (возникают ассоциации с r-value), ведь на деле это просто non_ref_type.
NB>>>ассоциация правильная. NB>>>при perfect forwarding'е у l-value будет тип T&
__>>и? вы, случаем, не путаете l-/r-/pr- values (имеющие все нессылочные типы non_ref_type) со значениями ссылок на них (имеющие ссылочные типы наподобие non_ref_type&, non_ref_type&&)?
NB>не расшифровал. NB>using был сделан для отделения rval от lval при perfect forwarding'е и он с этим справляется. чего еще нужно? NB>заменить название на non_ref_type -- значит ухудшить читабельность.
шаблон
template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
using rval = T;
инстанцируется в случае, когда T не является ссылочным, а не в случае, когда T является типом r-value. потому называть инстанцированный тип через rval, имхо, — сбивать с толку.
__>инстанцируется в случае, когда T не является ссылочным, а не в случае, когда T является типом r-value. потому называть инстанцированный тип через rval, имхо, — сбивать с толку.
не рассматривай этот шаблон в отрыве от функции, для которой он используется.
то есть это имя не только описание того что, но и описание того, для чего
у тебя же нет вопросов например к такому:
template<typename T>
using owner = T;
void foo(gsl::owner<int*> data);
__>>инстанцируется в случае, когда T не является ссылочным, а не в случае, когда T является типом r-value. потому называть инстанцированный тип через rval, имхо, — сбивать с толку.
NB>не рассматривай этот шаблон в отрыве от функции, для которой он используется. NB>то есть это имя не только описание того что, но и описание того, для чего
NB>у тебя же нет вопросов например к такому: NB>
owner не сбивает с толку, потому что у него только одна трактовка (которая потом станет понятна из контекста). а в вашем примере их несколько.
меня сразу сбило это с толку — начинаю читать код, и вижу, что название не соответствует тому, что код делает. в общем, имхо, это плохой стиль написания кода.
Здравствуйте, Videoman, Вы писали:
V>Давно этим пользуюсь. Код гораздо чище получается и его легче менять. Странно что вас это удивляет, ведь даже в вашем примере, сам std::enable_if_t — это и есть, такой же, шаблонный using от простого std::enable_if.
Тут фокус в том, что используется не только SFINAE, но и обратный вывод типов.
Почему-то думал, что эта фича будет доступна только начиная с C++17.
Однако, уже в 14 работает.
Здравствуйте, night beast, Вы писали:
NB>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE. NB>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Videoman, Вы писали:
V>>Давно этим пользуюсь. Код гораздо чище получается и его легче менять. Странно что вас это удивляет, ведь даже в вашем примере, сам std::enable_if_t — это и есть, такой же, шаблонный using от простого std::enable_if.
К>Тут фокус в том, что используется не только SFINAE, но и обратный вывод типов.
К>Почему-то думал, что эта фича будет доступна только начиная с C++17. К>Однако, уже в 14 работает.
К>http://ideone.com/0Dgjn7 К>
К>#include <iostream>
К>using namespace std;
К>template<class T> using ptr = T*;
К>template<class T>
К>void foo(ptr<T> p) { cout << __PRETTY_FUNCTION__ << endl; }
К>int main() {
К> int x = 1;
К> foo(&x);
К>// foo(x); // will not compile
К>}
К>
Кодт, а что конкретно подразумевается под "обратным выводом типа" в данном примере?
Здравствуйте, Кодт, Вы писали:
К>Тут фокус в том, что используется не только SFINAE, но и обратный вывод типов.
долго думал и надумал, что обратного вывода все же тут нет
тут нет зависимых типов и решения обратного уравнения.
вот когда ты напишешь вручную класс
A<X> { typedef X T; } и попытаешься вывести тип, вызывая функциию без укащзания шаблонных параметров
template <typename X>
void f(typename A<X>::T v)
, то увидишь облом, т.к. столкнулись с зависимыми типами и попыткой найти обратное решение (найти X)
тут хитрость в том, что юзинг задает такое выражение, для которого уже давно утверждено, что вывод произойдет успешно
см. C++11/C++14
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]
пункт 8
просто сверху еще обмазали SFINAE, то есть юзинг задал вроде простое выражение (см. что после знака "="), но оно иногда обламывается (см. второй тип с enable_if)
полностью механику я не понимаю, только на пальцах. прошу просветить знающих
Здравствуйте, uzhas, Вы писали:
U>просто сверху еще обмазали SFINAE, то есть юзинг задал вроде простое выражение (см. что после знака "="), но оно иногда обламывается (см. второй тип с enable_if) U>полностью механику я не понимаю, только на пальцах. прошу просветить знающих
Ну механика-то простая: часть параметров вывели сопоставлением типа, а другую часть надо откуда-то взять.
Вот взяли подстановкой дефолтных значений. Или не взяли, если не смогли.
С равным успехом можно было напихать SFINAE в (мета-)сигнатуру шаблона функции
template<class T,
class V = std::enable_if_t<sizeof(T)==1>, // void в случае успеха
std::enable_if_t<sizeof(T)==1, int> I = 123 // int в случае успеха,
>
void foo(T b) {}
template<class T>
void bar(T b, std::enable_it_t<sizeof(T)==1,int> i = 123) {}
Что, в общем-то, плохо — потому что можно нечаянно понапихать неконсистентного мусора при явном указании параметров шаблона
foo('a'); // тут всё на SFINAE
foo<long,char,456>(1U); // ни в склад, ни в лад!
bar('a'); // тут всё как задумано
bar('a', 0xDEADC0DE); // SFINAE будет работать, но мы изменили тип функции и сделали дырку в синтаксисе - позволяем вызывать с лишним аргументом
или вычислять ненужные параметры
template<class T, class V> using itself_t = T;
template<class T, int I> using itself_i = T;
template<class T> void buz(itself_t<T, std::enable_if_t<sizeof(T)==1> > b) {}
template<class T> void xyz(itself_i<T, std::enable_if_t<sizeof(T)==1,int>(123)> b) {}
Ну и наконец, чтобы не заниматься копипазмом, — вычисление дефолтных параметров можно перетащить в using.
template<class T, class V = std::enable_if_t<sizeof(T)==1> > using somebyte_t = T;
template<class T, std::enable_if_t<sizeof(T)==1,int> I = 1 > using somebyte_i = T;
template<class T> void ttt(somebyte_t<T> b);
template<class T> void iii(somebyte_i<T> b);
Несмотря на то, что злой буратино может написать somebyte_t<int,void> — он его не сможет подставить в ttt.
А вот somebyte_i<int,123> в принципе не получится написать!
Поэтому трюк с параметром-значением наиболее предпочтителен.