Я хочу больше углубиться в метапрограммирование шаблонов. Я знаю, что SFINAE означает «отказ замены не является ошибкой». Но может ли кто-нибудь показать мне хорошее применение SFINAE?
c++
templates
metaprogramming
sfinae
rlbond
источник
источник
Ответы:
Вот один пример ( отсюда ):
Когда
IsClassT<int>::Yes
вычисляется, 0 не может быть преобразован в,int int::*
потому что int не является классом, поэтому у него не может быть указателя на член. Если SFINAE не существует, вы получите ошибку компилятора, что-то вроде «0 не может быть преобразован в указатель на член для неклассового типа int». Вместо этого он просто использует...
форму, которая возвращает Two, и, таким образом, возвращает false, int не является типом класса.источник
...
, а скорее тоint C::*
, чего я никогда не видел, и мне пришлось поискать. Нашел ответ о том, что это такое и для чего его можно использовать здесь: stackoverflow.com/questions/670734/…Мне нравится использовать
SFINAE
для проверки логических условий.Это может быть весьма полезно. Например, я использовал его, чтобы проверить, не превышает ли список инициализаторов, собранный с использованием запятой оператора, фиксированный размер.
Список принимается, только если M меньше N, что означает, что в списке инициализатора не слишком много элементов.
Синтаксис
char(*)[C]
означает: указатель на массив с типом элемента char и размеромC
. ЕслиC
false (здесь 0), то мы получаем недопустимый типchar(*)[0]
, указатель на массив нулевого размера: SFINAE делает так, что тогда шаблон будет проигнорирован.Выражается так
boost::enable_if
, это выглядит такНа практике я часто считаю полезной способность проверять условия.
источник
M <= N ? 1 : -1
сработает.int foo[0]
. Я не удивлен, что он поддерживается, поскольку он позволяет использовать очень полезный трюк с «структурой, заканчивающейся массивом нулевой длины» ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).error C2466: cannot allocate an array of constant size 0
В C ++ 11 тесты SFINAE стали намного красивее. Вот несколько примеров распространенного использования:
Выберите перегрузку функции в зависимости от свойств
Используя так называемую идиому приемника типа, вы можете выполнять довольно произвольные тесты для типа, например, проверять, есть ли у него член и принадлежит ли этот член определенному типу.
Вот живой пример: http://ideone.com/dHhyHE Я также недавно написал целый раздел о SFINAE и отправке тегов в своем блоге (бесстыдный плагин, но актуально) http://metaporky.blogspot.de/2014/08/ часть-7-статическому Ударно-function.html
Обратите внимание, что в C ++ 14 есть std :: void_t, который по сути совпадает с моим TypeSink здесь.
источник
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
в одном месте, а затемTypeSinkT<decltype(&T::bar)>
в другом? Также&
необходимо вstd::declval<T&>
?TypeSink
, C ++ 17 естьstd::void_t
:)Библиотека enable_if Boost предлагает приятный чистый интерфейс для использования SFINAE. Один из моих любимых примеров использования - в библиотеке Boost.Iterator . SFINAE используется для включения преобразования типов итератора.
источник
C ++ 17, вероятно, предоставит универсальные средства для запроса функций. Подробности см. В N4502 , но в качестве самостоятельного примера рассмотрим следующее.
Эта часть является постоянной, поместите ее в заголовок.
Следующий пример, взятый из N4502 , показывает использование:
По сравнению с другими реализациями эта довольно проста: достаточно ограниченного набора инструментов (
void_t
иdetect
). Кроме того, сообщалось (см. N4502 ), что он заметно более эффективен (время компиляции и потребление памяти компилятора), чем предыдущие подходы.Вот живой пример , который включает настройки переносимости для GCC до 5.1.
источник
Вот еще один ( в конце) SFINAE пример, основанный на Грег Роджерс «S ответ :
Таким образом, вы можете проверить
value
значение, чтобы узнать,T
класс или нет:источник
int C::*
в вашем ответе? Как можетC::*
быть имя параметра?int C::*
- это тип указателя наint
переменную-членC
.Вот одна хорошая статья SFINAE: Введение в концепцию SFINAE в C ++: самоанализ члена класса во время компиляции .
Резюмируйте это следующим образом:
declval
- это утилита, которая дает вам «ложную ссылку» на объект типа, который нелегко построить.declval
действительно удобен для наших конструкций SFINAE.источник
Здесь я использую перегрузку функции шаблона (не непосредственно SFINAE), чтобы определить, является ли указатель указателем на функцию или класс-член: ( Можно ли исправить указатели функций-членов iostream cout / cerr, которые печатаются как 1 или true? )
https://godbolt.org/z/c2NmzR
Печать
Как и есть код, он может (в зависимости от «хорошей» воли компилятора) генерировать вызов функции во время выполнения, которая вернет истину или ложь. Если вы хотите принудительно
is_function_pointer(var)
вычислить тип компиляции (во время выполнения не выполняются вызовы функций), вы можете использоватьconstexpr
трюк с переменной:Согласно стандарту C ++, все
constexpr
переменные гарантированно оцениваются во время компиляции ( вычисление длины строки C во время компиляции. Это действительно constexpr? ).источник
В следующем коде используется SFINAE, чтобы компилятор мог выбрать перегрузку в зависимости от того, имеет ли тип определенный метод или нет:
Вывод:
источник