std::enable_if 的 C++ 可变参数模板部分模板特化
C++ variadic template partial template specialization with std::enable_if
ITNOA
我的问题是如何在可变参数模板部分模板特化场景中使用 std::enable_if
?
例如,我有一个 class 使用可变参数模板部分特化,如下所示
/**
* Common case.
*/
template<typename... Args>
struct foo;
/**
* Final superclass for foo.
*/
template<>
struct foo<>{ void func() {} };
/**
* Regular foo class.
*/
template<typename H, typename... T>
struct foo<H, T...> : public foo<T...> {
typedef super foo<T...>;
void func() {
cout << "Hi" << endl;
this->super::template func();
}
}
它工作正常,但如果 H
是整数类型,我想要特定的部分专业化,所以我添加如下新代码
enum class enabled {_};
template<typename H, typename... T>
struct foo<H, typename std::enable_if<std::integral_type<H>::value,enabled>::type = enabled::_, T...> : public foo<T...> {
typedef super foo<T...>;
void func() {
cout << "Hooray Inegral" << endl;
this->super::template func();
}
}
但是上面的代码不起作用,我的问题是如何像上面那样做?
enable_if<bool b, T=void>
是一个 class 模板,如果 b==true
则定义 type=T
。因此,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
中推断出 H
foo<int>
。它必须以某种方式推断出 enable_if
和 is_integral
的语义,并查看如果 H=int
,那么它会准确地解析为 foo<int>
,这当然不能在一般情况下完成。
SFINAE 只能应用于重载解析期间考虑的那些部分。第一个和您使用的那个是 class 模板参数,但正如我上面所说,这会改变它实际匹配的内容。另一个选项是模板参数本身。但是 C++ 不允许 class 专门化的默认模板参数,通常用于此。函数中没有 return 值。 SFINAE 不使用 class 正文或其基础 class 中的任何内容。我不认为你想要的是你当前的设置。
我将提供一个轻微的重新设计:
#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>
之间的任何共同行为提取到共同基础 class 中。当前 foo
和 foos
API 之间存在重复。但是 foo
' API 可以被视为私有的并且可以不同,所以我不会说这是缺点。正如我在 foo_impl
中解释的那样,Ts...
是必需的。如果您不需要它们,可以通过 - 例如通过简单地从 std::tuple<foo<Ts>...>
和一些折叠表达式 (C++17)/magic(c++14) 派生来调用 func
函数。如果你愿意,我可以补充。
ITNOA
我的问题是如何在可变参数模板部分模板特化场景中使用 std::enable_if
?
例如,我有一个 class 使用可变参数模板部分特化,如下所示
/**
* Common case.
*/
template<typename... Args>
struct foo;
/**
* Final superclass for foo.
*/
template<>
struct foo<>{ void func() {} };
/**
* Regular foo class.
*/
template<typename H, typename... T>
struct foo<H, T...> : public foo<T...> {
typedef super foo<T...>;
void func() {
cout << "Hi" << endl;
this->super::template func();
}
}
它工作正常,但如果 H
是整数类型,我想要特定的部分专业化,所以我添加如下新代码
enum class enabled {_};
template<typename H, typename... T>
struct foo<H, typename std::enable_if<std::integral_type<H>::value,enabled>::type = enabled::_, T...> : public foo<T...> {
typedef super foo<T...>;
void func() {
cout << "Hooray Inegral" << endl;
this->super::template func();
}
}
但是上面的代码不起作用,我的问题是如何像上面那样做?
enable_if<bool b, T=void>
是一个 class 模板,如果 b==true
则定义 type=T
。因此,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
中推断出 H
foo<int>
。它必须以某种方式推断出 enable_if
和 is_integral
的语义,并查看如果 H=int
,那么它会准确地解析为 foo<int>
,这当然不能在一般情况下完成。
SFINAE 只能应用于重载解析期间考虑的那些部分。第一个和您使用的那个是 class 模板参数,但正如我上面所说,这会改变它实际匹配的内容。另一个选项是模板参数本身。但是 C++ 不允许 class 专门化的默认模板参数,通常用于此。函数中没有 return 值。 SFINAE 不使用 class 正文或其基础 class 中的任何内容。我不认为你想要的是你当前的设置。
我将提供一个轻微的重新设计:
#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>
之间的任何共同行为提取到共同基础 class 中。当前 foo
和 foos
API 之间存在重复。但是 foo
' API 可以被视为私有的并且可以不同,所以我不会说这是缺点。正如我在 foo_impl
中解释的那样,Ts...
是必需的。如果您不需要它们,可以通过 - 例如通过简单地从 std::tuple<foo<Ts>...>
和一些折叠表达式 (C++17)/magic(c++14) 派生来调用 func
函数。如果你愿意,我可以补充。