Шаблон специализации для SFINAE - PullRequest
0 голосов
/ 12 сентября 2018

Я давно работаю с шаблонами, но недавно я столкнулся со странным программированием шаблонов для SFINAE. Мой вопрос, почему мы пишем typename = {something} как параметр шаблона в C ++?

Ответы [ 3 ]

0 голосов
/ 12 сентября 2018

Пример:

//for enum types.
template <typename T, typename std::enable_if<std::is_enum_v<T>, int>::type = 0>
void Foo(T value)
{
  //stuff..
}

Я хочу, чтобы этот Foo был выбран, только если T является типом перечисления. Мы можем достичь этого, используя правило «Заменяющая ошибка не является ошибкой» (SFINAE).


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

Итак, как мы можем убедиться, что Foo вызывается только с типом enum? Ну, это легко, мы находим способ вызвать ошибку замещения!

Если мы проверяем cppreference для std::enable_if, это говорит:

template< bool B, class T = void >
struct enable_if;

Если B равен true, std::enable_if имеет открытый член typedef type, равный до T; в противном случае нет члена typedef.

Это означает, что если std::is_enum<T>::value равно true (что относится к типам перечислений для T), тогда B будет true и, следовательно, type будет допустимым типом, int как указано.

Если, однако, std::is_enum::value равно false, то B будет false и, следовательно, type даже не будет существовать. В этом случае код пытается использовать ::type, пока он не существует, и поэтому это ошибка замещения. Таким образом, SFINAE включается и исключает его из списка кандидатов.


Причина, по которой мы используем = 0, заключается в предоставлении значения параметра шаблона по умолчанию, так как на самом деле мы не заинтересованы в использовании этого типа или его значения, мы используем его только для запуска SFINAE.

0 голосов
/ 12 сентября 2018

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

typename <identifier> [ = <type> ]

но идентификатор может быть опущен.

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

template <typename T, typename = std::enable_if<(some-constexpr-involving-T)>::type> ...

Если выражение имеет значение true, все хорошо: std::enable_if<true>::type это просто void, и у нас есть хорошая рабочая специализация шаблона.

Теперь, если приведенное выше выражение оказывается ложным, std::enable_if<false> не имеет члена с именем type, происходит Сбой замещения (SF в SFINAE) и специализация шаблона не рассматривается.

На этом роль параметра tge заканчивается. Он не используется ни для чего в самом шаблоне, поэтому ему не нужно имя.

0 голосов
/ 12 сентября 2018

Это просто, как работает SFINAE;)

Как вы знаете, вы должны "создать ошибку" в объявлении шаблона, а не в определении шаблона.Как это:

template < typename X, typename = ... here is the code which may generate an error     during instantiation >
void Bla() {}

Единственный шанс вставить некоторый «код» в объявление - это определить что-то в списке параметров шаблона или внутри самого объявления функции шаблона, например:

template < typename X>
void Bla( ... something which may generates an error ) {}

Пример:

template <typename T, typename = std::enable_if_t< std::is_same_v< int, T>>>
void Bla()
{
    std::cout << "with int" << std::endl;
}

template <typename T, typename = std::enable_if_t< !std::is_same_v< int, T>>>
void Bla(int=0)
{
    std::cout << "with something else" << std::endl;
}

int main()
{
    Bla<int>();
    Bla<std::string>();
}

Но каков здесь фон "создания ошибки замещения"?

Хитрость где-то позади std::enable_if.Мы также можем использовать его без этого:

template <typename T, typename = char[ std::is_same_v< int, T>]>
void Bla()
{   
    std::cout << "with int" << std::endl;
}   

template <typename T, typename = char[ !std::is_same_v< int, T>]>
void Bla(int=0)
{   
    std::cout << "with something else" << std::endl;
}   

Взгляните на: typename = char[ !std::is_same_v< int, T>] Здесь std::is_same_v возвращает нам значение bool, которое преобразуется в 0, если оно недействительно, и в любое положительное число, если оно допустимо.,А создание char[0] - это просто ошибка в c ++!Таким образом, с отрицанием нашего выражения с ! мы получили один для «is int» и один для «is int».Как только он пытается создать массив размера 0, что является ошибкой, и шаблон не будет создан, и как только он генерирует тип char[!0], который является допустимым выражением.

И в качестве последнего шага:

... typename = ...

подразумевается как: здесь будет определен тип, здесь без имени параметра шаблона, так как сам параметр не используется позже.Вы также можете написать:

... typename X = ... 

, но поскольку X не используется, оставьте его!

Резюме: Вы должны предоставить некоторый код, который генерирует ошибку или нет, в зависимости от типаили значение данного выражения.Выражение должно быть частью объявления функции или частью списка параметров шаблона. не разрешено помещать ошибку в определение функции / класса, так как это больше не будет "не ошибкой" в смысле SFINAE.

Обновление: могут результатыВыражения SFINAE используются для дальнейших выражений: Да

Пример:

    template < typename TYPE >
void CheckForType()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

    template <typename T, typename X = std::enable_if_t< std::is_same_v< int, T>, float>>
void Bla()
{
    std::cout << "with int" << std::endl;
    CheckForType<X>();
}

    template <typename T, typename X = std::enable_if_t< !std::is_same_v< int, T>, double >>
void Bla(int=0)
{
    std::cout << "with something else" << std::endl;
    CheckForType<X>();
}

int main()
{
    Bla<int>();
    Bla<std::string>();
}

Вывод:

with int
void CheckForType() [with TYPE = float]
with something else
void CheckForType() [with TYPE = double]

Объяснение: std::enable_if имеет второй параметр шаблона, который используетсяв качестве возвращаемого типа, если его первым параметром будет true.Как этот, вы можете использовать этот тип здесь.Как видите, функция CheckForType вызывается с этим определенным типом для X из выражения SFINAE.

...