C++ 带模板包的SFINAE:如果没有模板参数,则禁用成员
我想定义一个带有可选模板参数的类,以便:C++ 带模板包的SFINAE:如果没有模板参数,则禁用成员,c++,templates,enums,sfinae,C++,Templates,Enums,Sfinae,我想定义一个带有可选模板参数的类,以便: 如果参数存在并且是枚举类,则定义 运算符() 可以在不使用其他模板的情况下对类进行IStation 参数(在这种情况下,运算符()不存在) 目标是允许通过可选的枚举类访问数组元素 代码: 关于运算符()的定义。这是因为当使用单个参数表示数据时,未定义enum\u wrapper::type。 但是,在operator()的定义中,模板参数bool activate及其返回类型std::enable\u(如果时实例化operator())。。。T为空。
- 如果参数存在并且是枚举类,则定义 运算符()
- 可以在不使用其他模板的情况下对类进行IStation 参数(在这种情况下,运算符()不存在)
enum\u wrapper::type
。
但是,在operator()
的定义中,模板参数bool activate
及其返回类型std::enable\u(如果
是为了防止参数packtypename>时实例化operator()
)。。。T
为空。
为什么在SFINAE下不简单地放弃操作符()
的定义
另外,当enum_wrapper::type
不是enum类时,我想使用std::is_enum
进一步消除运算符()。
在什么情况下
std::enable_if<std::is_enum_v<(enum_wrapper<T...>::type)>>
std::启用
是否应该插入以使SFINAE工作 问题在于operator()
的参数类型不依赖于模板参数(tooperator()
)
您可以通过指定新参数包(必须与类相同)的间接寻址使其依赖(不幸的是,我们不能默认参数包,否则我们会)
然后我们可以这样称呼它:
data<3> a; // no operator()
data<3, int> b; // operator() viable
b.x[0] = 1;
b.x[1] = 3;
b.x[2] = 5;
std::cout << b(1) << std::endl; // print '3'
您可以像以前一样调用它,但仍然没有为a
定义操作符()。但是,如果尝试调用b(2.0)
,则会触发静态断言
我想说——与其在operator()
@SamVarshavchik中使用std::enable\u if
和制作operator()
模板,不如在operator()
中粘贴static\u assert
——定义“cleaner”。如果OP必须管理重载,SFINAE是唯一的方法。静态断言将导致错误,而不是优雅地忽略错误重载。这里没有提到重载()
。只有一个工作的()
操作符,这就是我解析它的方式。不使用参数包应该更简单,而是使用单个模板参数,typename E=void
,然后使用调用std::is_enum\u v
@SamVarshavchik的static\u assert
——我的分析是,应该优雅地删除生成的重载。如果你怀疑OP的目标走错了方向,请澄清目标。不要设定一个目标,对“未来化”做出笼统的陈述。你很容易被证明是错的。@Sam Varshavchik谢谢你的评论。我确实更愿意在无效时通过SFINAE放弃operator()
的定义;正如@StoryTeller所注意到的,这将允许更大的灵活性,例如在重载operator()
时。在enum_wrapper
中使用默认模板参数是一个可行的选项,本质上相当于@andyg在下面发布的解决方案感谢您的解决方案。我相信您对enum\u wrapper
的定义相当于将typename T0=int
设置为默认参数,对吗?我认为,定义< >代码>枚举包装器< /> >“代码> >枚举包装器< /> >没有<代码>类型字段,但不幸的是,SfayAe将不能工作,至少是用当前C++。”弗朗西斯科:是的。同意使用空枚举包装器。enum_包装器的空包缺乏专门化,这就是SFINAE中的问题所在。另一种选择是专门化整个类@francesco:参见我的更新帖子。当我们将fn的参数设置为模板参数时,我们可以实现适当的SFINAE,但随后我们会将检查推迟到函数中的static\u assert
。非常感谢!有趣地使用static\u assert
来克服SFINAE的(当前)限制。
std::enable_if<std::is_enum_v<(enum_wrapper<T...>::type)>>
template <bool activate = (sizeof...(T) > 0), typename... U>
std::enable_if_t<activate && std::conjunction_v<std::is_same<T, U>...>, int>&
operator()(const typename enum_wrapper<U...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
template<class...>
struct enum_wrapper{using type = int;};
template <typename T0, typename... T1>
struct enum_wrapper<T0, T1...> {
typedef T0 type;
};
//...
template <bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const typename enum_wrapper<T...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
data<3> a; // no operator()
data<3, int> b; // operator() viable
b.x[0] = 1;
b.x[1] = 3;
b.x[2] = 5;
std::cout << b(1) << std::endl; // print '3'
template <class U, bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const U& e)
{
static_assert(std::is_same_v<U, typename enum_wrapper<T...>::type>, L"argument type to operator() is incorrect");
return x[static_cast<std::size_t>(e)];
}