Я не могу четко понять, что это означает, когда упоминается, что определенная функция, структура или... SFINAE-friendly.
Кто-нибудь, пожалуйста, объясните это?
Я не могу четко понять, что это означает, когда упоминается, что определенная функция, структура или... SFINAE-friendly.
Кто-нибудь, пожалуйста, объясните это?
Если он допускает сбой замены без жесткой ошибки (как static_assert
).
например
template <typename T>
void call_f(const T& t)
{
t.f();
}
Функция объявляется для всех T
, даже те, у которых нет f
, поэтому вы не можете делать SFINAE на call_f<WithoutF>
по мере того, как этот метод существует. (Демо некомпилирующего кода).
Со следующим изменением:
template <typename T>
auto call_f(const T& t) ->decltype(t.f(), void())
{
t.f();
}
Метод существует только для действительного T. поэтому вы можете использовать SFINAE как
template<typename T>
auto call_f_if_available_impl(const T& t, int) -> decltype(call_f(t))
{
call_f(t);
}
template<typename T>
auto call_f_if_available_impl(const T& t, ...)
{
// Do nothing;
}
template<typename T>
auto call_f_if_available(const T& t)
{
call_f_if_available_impl(t, 0);
}
Обратите внимание, что int = 0
и ...
- это порядок перегрузки.
Демо
-
Другой случай, когда шаблон добавляет специальный параметр для применения SFINAE для специализации:
template <typename T, typename Enabler = void> struct S;
И затем
// Specialization only available for T which respect the traits.
template <typename T>
struct S<T, std::enable_if_t<my_type_trait<T>::value>>
{
};
Сущность называется SFINAE-дружественной, если ее можно использовать в контексте SFINAE без возникновения жесткой ошибки при ошибке замены. Я предполагаю, что вы уже знаете, что такое SFINAE, поскольку это целый другой вопрос сам по себе.
В контексте стандартизации С++ термин SFINAE-friendly до сих пор применялся к std::result_of
и std::common_type
. Возьмем следующий пример:
template <typename T>
void foo(T x, typename std::common_type<T, int>::type y) {}
void foo(std::string x, std::string y) {}
int main()
{
foo(std::string("hello"), std::string("world"));
}
Без SFINAE-дружественного common_type
это не скомпилируется, потому что std::common_type<std::string, int>::type
приведет к жесткой ошибке при замене аргумента шаблона. С введением SFINAE-дружественного common_type
(N3843) этот пример становится хорошо сформированным, потому что std::common_type<std::string, int>::type
производит сбой замены, поэтому что перегрузка исключена из жизнеспособного набора.
Здесь аналогичный пример с result_of
:
template <typename T>
auto bar(T f) -> typename std::result_of<T()>::type { return f(); }
void bar(int n) {}
int main()
{
bar(42);
}
Без SFINAE-дружественного result_of
это не скомпилируется, потому что std::result_of<int()>::type
приведет к жесткой ошибке при замене аргумента шаблона. С введением SFINAE-дружественного result_of
(N3462) этот пример становится хорошо сформированным, потому что std::result_of<int()>::type
производит сбой замены, поэтому что перегрузка исключена из жизнеспособного набора.