我想定义一个类,其中包含一个可选的模板参数:
目标是允许通过可选的枚举类访问数组元素。
代码:
#include <type_traits>
template <int N, typename... T>
class data {
int x[N];
template <typename T0, typename... T1>
struct enum_wrapper {
typedef T0 type;
};
public:
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)]; }
};
int main()
{
data<3> a;
return 0;
}
enum_wrapper的目的是挑出包typename... T
中的第一个参数。
使用g++ -std=c++14
编译的代码会出错
error: wrong number of template arguments (0, should be at least 1)
关于operator()的定义。这是因为当使用单个参数enum_wrapper<T...>::type
来数据化时,未定义<3>
。
但是,bool activate
定义中的模板参数operator()
及其返回类型std::enable_if_t<activate, int>
是为了防止参数包operator()
出现时typename... T
的即时消息空。
为什么operator()
的定义不能简单地在SFINAE下丢弃?
另外,我想在enum_wrapper::type
不是enum class
时使用std::is_enum
进一步消除operator()。
像
std::enable_if<std::is_enum_v<(enum_wrapper<T...>::type)>>
应插入以使SFINAE工作?
答案 0 :(得分:2)
问题是operator()
的参数类型不依赖于模板参数(operator()
)
你可以通过指向一个必须与类相同的新参数包的间接使它依赖(不幸的是我们不能默认参数包,否则我们会这样做)
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)]; }
我想我对你如何计划调用operator()
有一些顾虑,因为enum_wrapper
在课堂上是私人的。我认为您真正感兴趣的是接收与enum_wrapper<T...>::type
相同的类型。这里最简单的方法是为空enum_wrapper
简单地为T...
进行专门化,然后为其禁用operator()
:
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'
(我已将会员x
public
用于测试目的)
最后,可能更容易将operator()
的参数属于enum_wrapper<T...>::type
类型的条件移动到受{保护的函数内的static_assert
内{1}}:
bool activate
您可以像之前一样调用它,并且仍然没有为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)];
}
定义operator()
。但是,如果您尝试调用a
,则会触发静态断言。