Скотт Мейерс опубликовал содержание и статус своей следующей книги EC ++ 11. Он написал, что один пункт в книге может быть «Избегайте std::enable_if
подписей функций» .
std::enable_if
может использоваться в качестве аргумента функции, в качестве возвращаемого типа или в качестве шаблона класса или параметра шаблона функции для условного удаления функций или классов из разрешения перегрузки.
В этом вопросе показаны все три решения.
В качестве параметра функции:
template<typename T>
struct Check1
{
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, int>::value >::type* = 0) { return 42; }
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, double>::value >::type* = 0) { return 3.14; }
};
В качестве параметра шаблона:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if<
std::is_same<U, int>::value, int>::type = 0>
U read() { return 42; }
template<typename U = T, typename std::enable_if<
std::is_same<U, double>::value, int>::type = 0>
U read() { return 3.14; }
};
Как тип возврата:
template<typename T>
struct Check3
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
return 42;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
return 3.14;
}
};
- Какое решение должно быть предпочтительным и почему я должен избегать других?
- В каких случаях «Избегать
std::enable_if
в сигнатурах функций» касается использования в качестве возвращаемого типа (который не является частью обычной сигнатуры функции, а специализацией шаблона)? - Существуют ли различия для шаблонов функций-членов и не-членов?
std::enable_if
загромождать мои функции подписи (особенно некрасиво дополнительныйnullptr
аргумент функции версии) , потому что она всегда выглядит так , как это, странное хак (для чего - тоstatic if
могуществе сделать намного более красивым и чистым), используя шаблонную чёрную магию, чтобы использовать интересную языковую функцию. Вот почему я предпочитаю диспетчеризацию тегов, когда это возможно (ну, у вас все еще есть дополнительные странные аргументы, но не в общедоступном интерфейсе, а также гораздо менее уродливые и загадочные ).=0
вtypename std::enable_if<std::is_same<U, int>::value, int>::type = 0
достижении? Я не мог найти правильные ресурсы, чтобы понять это. Я знаю, что первая часть раньше=0
имела тип члена,int
еслиU
иint
такой же. Большое спасибо!Ответы:
Поместите взлом в параметры шаблона .
enable_if
От параметра шаблона подход имеет как минимум два преимущества по сравнению с другими:удобочитаемость : использование enable_if и типы return / аргумент не объединяются в один беспорядочный кусок неоднозначников typename и доступа к вложенным типам; даже несмотря на то, что беспорядок неоднозначности и вложенного типа может быть уменьшен с помощью шаблонов псевдонимов, это все равно объединит две несвязанные вещи. Использование enable_if связано с параметрами шаблона, а не с типами возврата. Наличие их в параметрах шаблона означает, что они ближе к тому, что имеет значение;
универсальная применимость : конструкторы не имеют возвращаемых типов, а некоторые операторы не могут иметь дополнительных аргументов, поэтому ни один из двух других параметров не может быть применен везде. Помещение enable_if в параметр шаблона работает везде, так как в любом случае вы можете использовать только SFINAE для шаблонов.
Для меня аспект читабельности является большим мотивирующим фактором в этом выборе.
источник
FUNCTION_REQUIRES
макроса делает его более приятным для чтения, и он также работает в компиляторах C ++ 03, а также полагается на использование в возвращаемом типе. Кроме того, использование параметров шаблона функции вызывает проблемы с перегрузкой, поскольку теперь сигнатура функции не является уникальной, вызывая неоднозначные ошибки перегрузки.enable_if
enable_if
enable_if
со стандартным параметром нетипового шаблона, который допускает перегрузку. Т.е.enable_if_t<condition, int> = 0
вместоtypename = enable_if_t<condition>
.flamingdangerzone
ссылка в вашем комментарии, похоже, ведет на страницу установки шпионских программ. Я отметил это для модераторского внимания.std::enable_if
при выводе аргумента шаблона опирается на принцип «Ошибка субстанции не является ошибкой » (он же SFINAE) . Это очень хрупкая языковая функция, и вы должны быть очень осторожны, чтобы сделать это правильно.enable_if
содержит вложенный шаблон или определение типа (подсказка: ищите::
токены), то разрешение этих вложенных храмов или типов обычно представляет собой не выводимый контекст . Любой сбой подстановки в таком невыбранном контексте является ошибкой .enable_if
перегрузках не могут иметь никакого перекрытия, потому что разрешение перегрузки было бы неоднозначным. Это то, что вы, как автор, должны проверить сами, хотя вы получите хорошие предупреждения компилятора.enable_if
манипулирует набором жизнеспособных функций во время разрешения перегрузки, которые могут иметь неожиданные взаимодействия в зависимости от присутствия других функций, которые вводятся из других областей (например, через ADL). Это делает его не очень надежным.Короче говоря, когда это работает, это работает, но когда это не так, это может быть очень трудно для отладки. Очень хорошая альтернатива - использовать диспетчеризацию тегов , то есть делегировать функции реализации (обычно в
detail
пространстве имен или в классе помощника), которая получает фиктивный аргумент, основанный на том же условии времени компиляции, которое вы используете вenable_if
.Диспетчеризация тегов не управляет набором перегрузки, но помогает вам выбрать именно ту функцию, которую вы хотите, предоставляя правильные аргументы через выражение времени компиляции (например, в признаке типа). По моему опыту, это намного легче отладить и получить права. Если вы начинающий автор библиотеки со сложными чертами типа, вам может потребоваться
enable_if
как-то, но для большинства регулярных условий компиляции это не рекомендуется.источник
enable_if
это сделать правильно?is_f_able
- это то, что я считаю задачей для авторов библиотек, которые, конечно, могут использовать SFINAE, когда это дает им преимущество, но для «обычных» пользователей и с учетом этой чертыis_f_able
я думаю, что диспетчеризация тегов проще.Параметр шаблона
Это может легко использоваться неправильно и приводит к ошибкам с перегрузками:
Обратите внимание,
typename = std::enable_if_t<cond>
вместо правильногоstd::enable_if_t<cond, int>::type = 0
тип возврата:
Последнее, в параметре функции:
+
,-
,*
, ...)void* = nullptr
) (чтобы указатель на функцию отличался и т. Д.)Есть тонкие различия с наследованием и
using
:По словам
using-declarator
(акцент мой):namespace.udecl
Таким образом, как для аргумента шаблона, так и для возвращаемого типа методы скрыты в следующем сценарии:
Демо (gcc ошибочно находит базовую функцию).
В то время как с аргументом, аналогичный сценарий работает:
демонстрация
источник