SFINAE with template pack:如果没有模板参数,则禁用成员

时间:2017-10-26 12:41:45

标签: c++ templates enums sfinae

我想定义一个类,其中包含一个可选的模板参数:

  • 如果参数存在且它是枚举类,则定义一个 运算符()
  • 可以在没有附加模板的情况下实现该类 参数(在这种情况下operator()不存在)

目标是允许通过可选的枚举类访问数组元素。

代码:

#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工作?

1 个答案:

答案 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)]; }

Demo

我想我对你如何计划调用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'

Better Demo

(我已将会员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,则会触发静态断言。

Best Demo