C++ 2: SFINAE
description
Transcript of C++ 2: SFINAE
![Page 1: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/1.jpg)
Лекция 9. SFINAE
Valery Lesin. C++ In-Depth, 2014 1
![Page 2: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/2.jpg)
Выбор кода в зависимости от типа
• Самый простой вариант – перегрузка функции
Valery Lesin. C++ In-Depth, 2014 2
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
struct currency_value
{
string name;
double value;
};
// by overloading
ostream& operator<<(ostream&, int)
{ /*...*/ }
ostream& operator<<(ostream& os, currency_value v)
{ os << "(" << v.name << "; " << v.value << ")"; }
![Page 3: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/3.jpg)
Выбор кода в зависимости от типа
• Выбор класса по типу используемому типу
можно осуществить специализацией шаблона
Valery Lesin. C++ In-Depth, 2014 3
1.
2.
3.
4.
5.
6.
7.
8.
// by specialization
template<class T, class A>
struct my_array
{ /*...*/ };
template<class A>
struct my_array<bool, A>
{ /*...*/ };
![Page 4: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/4.jpg)
Выбор типа по условию
• Как осуществить выбор типа по заданному
условию?
Valery Lesin. C++ In-Depth, 2014 4
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
template<class T>
struct sequence
{
vector<T> data_;
};
template<class T, bool is_polymorph>
struct sequence
{
typedef /* ??? */ value_type;
vector<value_type> data_;
};
![Page 5: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/5.jpg)
Выбор типа по условию
• Простое решение – специализация класса.
• Плохо масштабируется – придется
– выделять общую базу,
– для каждого подобного использования делать специализацию
Valery Lesin. C++ In-Depth, 2014 5
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
template<class T, bool is_polymorph>
struct sequence
{
typedef T* value_type;
vector<value_type> data_;
};
template<class T>
struct sequence<T, false>
{
typedef T value_type;
vector<value_type> data_;
};
![Page 6: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/6.jpg)
Выбор типа по условию
• Можно сделать специальную метафункцию,
предоставляющую выбор типа по условию
Valery Lesin. C++ In-Depth, 2014 6
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
template<bool take_first, class T1, class T2>
struct select
{ typedef T1 type; };
template<class T1, class T2>
struct select<false, T1, T2>
{ typedef T2 type; };
// result
template<class T, bool polymorph>
struct sequence
{
typedef select<polymorph, T*, T>::type value_type;
vector<value_type> data_;
};
![Page 7: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/7.jpg)
Проверка приводимости
• Хотим проверить, является ли B - открытым базовым классам для D?
• Попробуем это сделать через перегрузку функции.
• Обратите внимание - нет определений функций.
Valery Lesin. C++ In-Depth, 2014 7
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
typedef char yes;
typedef struct { yes dummy[2]; } no;
yes check(B);
no check(...);
// why not this way?
template<class T>
no check(T);
const conv_exist = sizeof(check(D())) == sizeof(yes);
![Page 8: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/8.jpg)
Проверка на базовый класс• Оформим в виде класса или макроса.
Valery Lesin. C++ In-Depth, 2014 8
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
template<class D, class B>
class converts_to
{
typedef charyes;
typedef struct { yes dm[2]; }no;
static yes check(B const&);
static no check(...);
static D makeD();
public:
// by agreement used in STL
enum { value = sizeof(check(makeD())) == sizeof(yes) };
};
#define BASE_N_DERIVED(B, D) \
converts_to <const D*, const B*>::value && \
!is_same_type<const B*, const void*>::value;
![Page 9: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/9.jpg)
SFINAE
• SFINAE - Substitutaion Failure Is Not An Error
• При определении перегрузки функции ошибочное инстанцирование шаблонов не вызывают ошибку компиляции, а выбрасывает функцию из списка кандидатов на наиболее подходящую перегрузку.
• SFINAE работает только с перегрузкой функций.
• Перегрузки могут отбрасываться в том случае, когда их невозможно инстанциировать из-за возникающей синтаксической ошибки - компиляция при этом продолжается без ошибок.
• Но не забывайте предоставить альтернативу.
• SFINAE рассматривает только заголовок функции, ошибки в теле функции не будут пропущены.
Valery Lesin. C++ In-Depth, 2014 9
![Page 10: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/10.jpg)
Проверка на контейнер• Проверим наличие методов begin и end.
• Работает для vector и list, но не для set или map. Почему?
Valery Lesin. C++ In-Depth, 2014 10
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
template<class T>
struct has_begin_end
{
typedef char yes;
typedef struct { yes dm[2]; } no;
template<
class U,
typename U::iterator (U::*)(),
typename U::iterator (U::*)()>
struct checker{};
template<class U>
static yes check(checker<U, &U::begin, &U::end>*);
template<class U>
static no check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(yes) };
};
![Page 11: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/11.jpg)
Проверка на контейнер
• Можно проверить даже на наличие функции, полученной от базового класса. Для этого воспользуемся decltype.
Valery Lesin. C++ In-Depth, 2014 11
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
template <class T>
struct has_cbegin_cend
{
typedef char yes;
typedef struct { yes dummy[2]; }no;
template <typename U>
static auto test(U* u)
-> decltype((*u).cbegin(), (*u).cend(), yes());
static no test(...);
enum { value = (sizeof(yes) == sizeof test((T*)0)) };
};
![Page 12: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/12.jpg)
Выбор по свойству типа
• В STL и Boost несколько отличаются эти типы.
std::enable_if == boost::enable_if_c
• В Boost также есть [lazy_] {enable/disable}_if [_c]
Valery Lesin. C++ In-Depth, 2014 12
1.
2.
3.
4.
5.
6.
7.
8.
9.
template <bool Cond, class T = void>
struct enable_if
{
typedef T type;
};
template <class T>
struct enable_if<false, T>
{};
![Page 13: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/13.jpg)
Выбор по свойству типа
• Возвращать или передавать?
– из конструкторов нельзя ничего вернуть
– операторы часто фиксируются количество переданных им
параметров
– оператор приведения должен возвращать зафиксированный тип
– в остальных случаях есть выбор!Valery Lesin. C++ In-Depth, 2014 13
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
template<class T>
enable_if<has_begin_end<T>::value>::type
my_print(T const& cont)
{
for(auto const& item: cont)
cout << item << " ";
}
template<class T>
void my_print(
T const& value,
enable_if<!has_begin_end<T>::value>::type* = 0)
{ cout << value; }
![Page 14: C++ 2: SFINAE](https://reader035.fdocuments.us/reader035/viewer/2022081907/54900410b47959763e8b4e79/html5/thumbnails/14.jpg)
Вопросы?
Valery Lesin. C++ In-Depth, 2014 14