Всегда ли необходимо дублировать параметр шаблона класса при включении конструкторов на его основе?
Вообще при использовании enable_if
на основе типа шаблона класса необходимо продублировать тип шаблона класса в качестве параметра шаблона конструктора или метода:
template <
typename U = T,
typename = typename std::enable_if<
!std::is_void<U>::value
>::type
>
Class() { }
Когда именно это необходимо (или нет)?
Например, следующий код прекрасно компилируется на G++, Clang и VC++...
template <typename T = void>
class Class {
public:
template <
typename U,
typename = typename std::enable_if<
// Is the use of T here allowed?
std::is_void<T>::value
|| std::is_base_of<T, U>::value
>::type
>
Class(U &&arg) {
std::cout << "Derived" << std::endl;
}
template <
typename U,
typename ...U_Rest,
typename = typename std::enable_if<
// Is the use of T here allowed?
!std::is_void<T>::value
&& !std::is_base_of<T, U>::value
>::type
>
Class(U &&arg, U_Rest &&...rest) {
std::cout << "Not Derived" << std::endl;
}
};
... но использует T
непосредственно как часть enable_if
, В частности, если T
является void
, версия конструктора "Производная" будет всегда включена, а версия "Не производная" всегда будет отключена, независимо от параметров U
,
Действительно ли вышесказанное действительно законно в соответствии со стандартом? Или компиляторы просто принимают это, вероятно, из-за "не требуется никакой диагностики"?
1 ответ
SFINAE применяется к шаблонному методу, и он не должен быть серьезным сбоем (поэтому в основном должен зависеть от его шаблонных параметров).
Здесь ваше состояние зависит от U
так что все в порядке.
Обратите внимание, что вы должны предпочесть
template <typename U, std::enable_if_t<cond>* = nullptr>
над
template <typename U, typename = std::enable_if_t<cond>>
разрешить писать отключенную версию
template <typename U, std::enable_if_t<!cond>* = nullptr>
как
template <typename U, typename = std::enable_if_t<cond>> //...
template <typename U, typename = std::enable_if_t<!cond>> // ...
иметь такую же подпись.