C++ 2: SFINAE 

14
Лекция 9. SFINAE Valery Lesin. C++ In-Depth, 2014 1

description

 

Transcript of C++ 2: SFINAE 

Page 1: C++ 2: SFINAE 

Лекция 9. SFINAE

Valery Lesin. C++ In-Depth, 2014 1

Page 2: C++ 2: SFINAE 

Выбор кода в зависимости от типа

• Самый простой вариант – перегрузка функции

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 

Выбор кода в зависимости от типа

• Выбор класса по типу используемому типу

можно осуществить специализацией шаблона

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 

Выбор типа по условию

• Как осуществить выбор типа по заданному

условию?

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 

Выбор типа по условию

• Простое решение – специализация класса.

• Плохо масштабируется – придется

– выделять общую базу,

– для каждого подобного использования делать специализацию

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 

Выбор типа по условию

• Можно сделать специальную метафункцию,

предоставляющую выбор типа по условию

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 

Проверка приводимости

• Хотим проверить, является ли 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 

Проверка на базовый класс• Оформим в виде класса или макроса.

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 

SFINAE

• SFINAE - Substitutaion Failure Is Not An Error

• При определении перегрузки функции ошибочное инстанцирование шаблонов не вызывают ошибку компиляции, а выбрасывает функцию из списка кандидатов на наиболее подходящую перегрузку.

• SFINAE работает только с перегрузкой функций.

• Перегрузки могут отбрасываться в том случае, когда их невозможно инстанциировать из-за возникающей синтаксической ошибки - компиляция при этом продолжается без ошибок.

• Но не забывайте предоставить альтернативу.

• SFINAE рассматривает только заголовок функции, ошибки в теле функции не будут пропущены.

Valery Lesin. C++ In-Depth, 2014 9

Page 10: C++ 2: SFINAE 

Проверка на контейнер• Проверим наличие методов 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 

Проверка на контейнер

• Можно проверить даже на наличие функции, полученной от базового класса. Для этого воспользуемся 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 

Выбор по свойству типа

• В 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 

Выбор по свойству типа

• Возвращать или передавать?

– из конструкторов нельзя ничего вернуть

– операторы часто фиксируются количество переданных им

параметров

– оператор приведения должен возвращать зафиксированный тип

– в остальных случаях есть выбор!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 

Вопросы?

Valery Lesin. C++ In-Depth, 2014 14