using for SFINAE
От: night beast СССР  
Дата: 02.03.17 07:44
Оценка: 135 (8)
Сегодня в группе 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);
}
Отредактировано 02.03.2017 7:50 night beast . Предыдущая версия .
Re: using for SFINAE
От: Videoman Россия https://hts.tv/
Дата: 02.03.17 08:14
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>
NB>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>using rval = T;
NB>...
NB>


Давно этим пользуюсь. Код гораздо чище получается и его легче менять. Странно что вас это удивляет, ведь даже в вашем примере, сам std::enable_if_t — это и есть, такой же, шаблонный using от простого std::enable_if.
Re[2]: using for SFINAE
От: night beast СССР  
Дата: 02.03.17 08:46
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Давно этим пользуюсь. Код гораздо чище получается и его легче менять. Странно что вас это удивляет, ведь даже в вашем примере, сам std::enable_if_t — это и есть, такой же, шаблонный using от простого std::enable_if.


да даже не задумывался о такой возможности
привык все через enable_if решать.
Отредактировано 02.03.2017 8:48 night beast . Предыдущая версия .
Re: using for SFINAE
От: _hum_ Беларусь  
Дата: 02.03.17 09:19
Оценка:
Здравствуйте, night beast, Вы писали:


NB>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>
NB>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>using rval = T;
...
NB>


а почему название для типа rval? как-то очень сбивает с толку (возникают ассоциации с r-value), ведь на деле это просто non_ref_type.


а идея, да,сама по себе интересная.
Re[2]: using for SFINAE
От: night beast СССР  
Дата: 02.03.17 09:30
Оценка:
Здравствуйте, _hum_, Вы писали:

NB>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>>
NB>>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>>using rval = T;
__>...
NB>>


__>а почему название для типа rval? как-то очень сбивает с толку (возникают ассоциации с r-value), ведь на деле это просто non_ref_type.


ассоциация правильная.
при perfect forwarding'е у l-value будет тип T&
Re[3]: using for SFINAE
От: _hum_ Беларусь  
Дата: 02.03.17 09:43
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, _hum_, Вы писали:


NB>>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>>>
NB>>>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>>>using rval = T;
__>>...
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&&)?
Re[4]: using for SFINAE
От: night beast СССР  
Дата: 02.03.17 09:50
Оценка:
Здравствуйте, _hum_, Вы писали:

NB>>>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>>>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>>>>
NB>>>>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>>>>using rval = T;
__>>>...
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 -- значит ухудшить читабельность.
Re[5]: using for SFINAE
От: _hum_ Беларусь  
Дата: 02.03.17 10:28
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, _hum_, Вы писали:


NB>>>>>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>>>>>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>>>>>
NB>>>>>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>>>>>using rval = T;
__>>>>...
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, имхо, — сбивать с толку.
Re[6]: using for SFINAE
От: night beast СССР  
Дата: 02.03.17 10:39
Оценка:
Здравствуйте, _hum_, Вы писали:

__>шаблон

__>
__>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
__>using rval = T;
__>

__>инстанцируется в случае, когда T не является ссылочным, а не в случае, когда T является типом r-value. потому называть инстанцированный тип через rval, имхо, — сбивать с толку.

не рассматривай этот шаблон в отрыве от функции, для которой он используется.
то есть это имя не только описание того что, но и описание того, для чего

у тебя же нет вопросов например к такому:
template<typename T>
using owner = T;

void foo(gsl::owner<int*> data);
Re[7]: using for SFINAE
От: _hum_ Беларусь  
Дата: 02.03.17 10:47
Оценка: +1
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, _hum_, Вы писали:


__>>шаблон

__>>
__>>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
__>>using rval = T;
__>>

__>>инстанцируется в случае, когда T не является ссылочным, а не в случае, когда T является типом r-value. потому называть инстанцированный тип через rval, имхо, — сбивать с толку.

NB>не рассматривай этот шаблон в отрыве от функции, для которой он используется.

NB>то есть это имя не только описание того что, но и описание того, для чего

NB>у тебя же нет вопросов например к такому:

NB>
NB>template<typename T>
NB>using owner = T;

NB>void foo(gsl::owner<int*> data);
NB>


owner не сбивает с толку, потому что у него только одна трактовка (которая потом станет понятна из контекста). а в вашем примере их несколько.
меня сразу сбило это с толку — начинаю читать код, и вижу, что название не соответствует тому, что код делает. в общем, имхо, это плохой стиль написания кода.
Re[2]: using for SFINAE
От: Кодт Россия  
Дата: 02.03.17 12:00
Оценка:
Здравствуйте, 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
}
Перекуём баги на фичи!
Re: using for SFINAE
От: Кодт Россия  
Дата: 02.03.17 12:12
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Сегодня в группе std-proposals наткнулся на интересное применение using для SFINAE.

NB>Раньше не задумывался о такой возможности. Возможно кому-нибудь еще пригодится:

NB>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>using rval = T;

ideone С++14 ругается, если у второго параметра нет имени.
template<class T, std::enable_if_t<CONDITION_GOES_HERE, ANY_SIMPLE_TYPE> V = ANY_SIMPLE_VALUE>  // bool = true, int = 12345 и т.п.
using checked = T;
Перекуём баги на фичи!
Re[2]: using for SFINAE
От: night beast СССР  
Дата: 02.03.17 12:24
Оценка:
Здравствуйте, Кодт, Вы писали:

К>
NB>>template<class T, std::enable_if_t<!std::is_reference<T>::value, bool> = true>
NB>>using rval = T;
К>

К>ideone С++14 ругается, если у второго параметра нет имени.
К>
К>template<class T, std::enable_if_t<CONDITION_GOES_HERE, ANY_SIMPLE_TYPE> V = ANY_SIMPLE_VALUE>  // bool = true, int = 12345 и т.п.
К>using checked = T;
К>


у меня нормально:
http://ideone.com/vZ9tWh
Re[3]: using for SFINAE
От: Кодт Россия  
Дата: 02.03.17 12:28
Оценка:
Здравствуйте, night beast, Вы писали:

NB>у меня нормально:

NB>http://ideone.com/vZ9tWh

Колдунство Видимо, когда я экспериментировал, то либо опечатался, либо сайт сглючил.
Перекуём баги на фичи!
Re[3]: using for SFINAE
От: _hum_ Беларусь  
Дата: 02.03.17 12:48
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, 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
К>}
К>


Кодт, а что конкретно подразумевается под "обратным выводом типа" в данном примере?
Re[3]: using for SFINAE
От: uzhas Ниоткуда  
Дата: 02.03.17 15:16
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Тут фокус в том, что используется не только 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)
полностью механику я не понимаю, только на пальцах. прошу просветить знающих
Re[4]: using for SFINAE
От: Кодт Россия  
Дата: 02.03.17 15:26
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Кодт, а что конкретно подразумевается под "обратным выводом типа" в данном примере?


Это я малость протупил.
Восстановление параметров шаблона по зависимому имени
template<class T> struct foo { using bar = T*; };
template<class T> using foobar = foo<T>::bar;

template<class T> void buz(foobar<T> t) {}

int main() {
  buz<int>(nullptr_t); // void(int*)

  int x;
  buz(&x);  // int* = foobar<T> = foo<T>::bar - сопоставление зависимых имён
            // foo<T>::bar = T* - это если нам повезло, C++17+ и нет специализаций foo.
            // T = int
}

В случае с using = какой-то-алгебраический-тип нет зависимых имён. Берём, сопоставляем.
template<class T> using foobar = T*;
...
buz(&x);  // int* = T*
Перекуём баги на фичи!
Re[4]: using for SFINAE
От: Кодт Россия  
Дата: 02.03.17 16:03
Оценка:
Здравствуйте, 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> в принципе не получится написать!

Поэтому трюк с параметром-значением наиболее предпочтителен.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.