enable_if<bool b, T=void>
- это шаблон класса, который определяет type=T
, если b==true
.Таким образом, enable_if<b>::type
является допустимым выражением, только если b==true
.SFINAE заявляет, что ошибка замены не является ошибкой.ИМХО not_disable_if
будет более подходящим именем.
Частичная специализация работает путем сопоставления шаблонов с текущим разрешенным типом.Вы не можете добавить новые аргументы шаблона в него, потому что он будет соответствовать чему-то другому.struct foo<H,std::enable_if_t<...,void>>
соответствует foo<H,void>
, только если ...
вычитается из H
и оценивается в true
.
struct foo<std::enable_if_t<std::is_integral_v<H>,H>>
не может работать просто потому, что нет способа вывести H
из, например, foo<int>
.Он должен был бы каким-то образом вывести семантику enable_if
и is_integral
и увидеть, что если H=int
, то он разрешается точно в foo<int>
, что, конечно, не может быть сделано в общем.
SFINAE может толькоприменяться только к тем частям, которые рассматриваются при разрешении перегрузки.Первый и тот, который вы использовали, это аргументы шаблона класса, но, как я уже говорил выше, это изменит то, что на самом деле соответствует.Другой вариант - сами параметры шаблона.Но C ++ запрещает аргументы шаблона по умолчанию для специализации классов, которые обычно используются для этого.Там нет возвращаемого значения, как в функциях.SFINAE не использует ничего внутри тела класса или его базовых классов.Я не думаю, что вам нужна текущая настройка.
Я предложу небольшой редизайн:
#include <iostream>
#include <type_traits>
// Only specifies behaviour for the head part - one T
// Ts... can be ignored, but are required.
// - I left them because I'm not sure whether you want to use them in the real code.
// - But they are required because `foos` use them as base classes and they must be unique.
template<typename T,bool is_integral,typename...Ts>
struct foo_impl;
template<typename T,typename...Ts>
struct foo_impl<T,true,Ts...>
{
void func() {
std::cout << "Hooray Inegral" << std::endl;
}
};
template<typename T,typename...Ts>
struct foo_impl<T,false,Ts...>
{
void func() {
std::cout << "Hi" << std::endl;
}
};
template<typename T,typename...Ts>
using foo = foo_impl<T,std::is_integral<T>::value,Ts...>;
template<typename...Ts>
struct foos;
template<typename H,typename...Ts>
struct foos<H,Ts...> : private foo<H,Ts...>, public foos<Ts...>
{
using head = foo<H,Ts...>;
using tail = foos<Ts...>;
//Only named differently for clarity, feel free to rename it back to 'func'
void rec_func()
{
//If we inherited from foo<H> so this was only this->foo<H>::func() then
//foos<int,int> would have two foo<int> base classes and this call would be ambigious.
this->head::func();
this->tail::rec_func();
}
};
template<> struct foos<>{
void rec_func(){}
};
int main()
{
foos<int,int,char,double> x;
x.rec_func();
}
* foo
имеет дело только с одним T
иимеет необходимые специализации.Вы можете извлечь любое общее поведение между foo<T,false>
и foo<T,true>
в общий базовый класс.В настоящее время существует дублирование между foo
и foos
API.Но foo
'API может рассматриваться как частный и может отличаться, поэтому я бы не сказал, что это вообще недостаток.Как я объяснил Ts...
в foo_impl
требуется.Если они вам не нужны, они могут быть удалены, например, путем простого извлечения из std::tuple<foo<Ts>...>
и некоторых выражений свертывания (C ++ 17) / magic (c ++ 14) для вызова функций func
.Я могу добавить это, если хотите.