在参数类型中存在或不存在POD结构成员时专门化模板功能

时间:2013-01-08 19:02:39

标签: c++ templates c++11 sfinae

给出一般形式的POD结构

struct case_0   { const char *foo;                       };
struct case_1i  { const char *foo; int v0;               };
struct case_1d  { const char *foo; double v0;            };
struct case_2ii { const char *foo; int v0;    int v1;    };
struct case_2id { const char *foo; int v0;    double v1; };
// etc

是否可以根据v0v1等数据成员的存在或不存在调度到(模板)函数重载集的成员 - 理想情况下,没有依赖于这些成员的具体类型 - 如果是,如何?具体而言,给出

void
process(const case_0& c)
{
   do_stuff_with(c.foo);
}

template <typename case_1> void   
process(const case_1& c)
{
   do_stuff_with(c.foo, c.v0);
}

template <typename case_2> void
process(const case_2& c)
{
   do_stuff_with(c.foo, c.v0, c.v1);
}

我希望为所有case_*结构选择每个重载,这些结构具有在其体内使用的所有v-成员,并且 - 同样重要 - 不< / em>拥有未在其正文中使用的任何v-个成员。

此程序必须100%自包含,所以请不要提升。 C ++ 11的功能还可以。

2 个答案:

答案 0 :(得分:3)

您需要编写一组特征,例如has_v0has_v1(我确信已在SO上多次演示过)然后使用它们限制您的重载:

template <typename case_0,
  typename = typename std::enable_if<!has_v0<case_0>::value>::type,
  typename = typename std::enable_if<!has_v1<case_0>::value>::type
>
void
process(const case_0& c)
{
   do_stuff_with(c.foo);
}

template <typename case_1,
  typename = typename std::enable_if<has_v0<case_1>::value>::type,
  typename = typename std::enable_if<!has_v1<case_1>::value>::type
>
void   
process(const case_1& c)
{
   do_stuff_with(c.foo, c.v0);
}

template <typename case_2,
  typename = typename std::enable_if<has_v0<case_2>::value>::type,
  typename = typename std::enable_if<has_v1<case_2>::value>::type
>
void
process(const case_2& c)
{
   do_stuff_with(c.foo, c.v0, c.v1);
}

您可以使用

之类的内容简化约束
template<typename Cond>
  using Require = typename std::enable_if<Cond::value>::type;

e.g。

template <typename case_2,
  typename = Require<has_v0<case_2>>,
  typename = Require<has_v1<case_2>>
>
void
process(const case_2& c)
{
   do_stuff_with(c.foo, c.v0, c.v1);
}

答案 1 :(得分:2)

@Jonathan Wakely提供了一种解决方案,它使用has_XXX元函数。

这是另一种解决方案,但它要求您将完整的fledge结构定义更改为仅std::tuple<>的typedef。

完整的结构:

struct case_0   { const char *foo;                       };
struct case_1i  { const char *foo; int v0;               };
struct case_1d  { const char *foo; double v0;            };
struct case_2ii { const char *foo; int v0;    int v1;    };
struct case_2id { const char *foo; int v0;    double v1; };

替换为typedefs结构,如下所示:

typedef std::tuple<const char*>            case_0;
typedef std::tuple<const char*,int>        case_1i;
typedef std::tuple<const char*,double>     case_1d;
typedef std::tuple<const char*,int,int>    case_2ii;
typedef std::tuple<const char*,int,double> case_2id;

template<typename...Args>
auto foo(std::tuple<Args...> & tpl) -> decltype(std::get<0>(tpl))&
{
     return std::get<0>(tpl);
}

template<typename...Args>
auto v0(std::tuple<Args...> & tpl) -> decltype(std::get<1>(tpl))&
{
     return std::get<1>(tpl);
}

template<typename...Args>
auto v1(std::tuple<Args...> & tpl) -> decltype(std::get<2>(tpl))&
{
     return std::get<2>(tpl);
}

和用法

case_1i obj; //full-fledge struct
obj.foo = "hello";
obj.v0 = 100;

替换为

case_1i obj; //typedef struct
foo(obj) = "hello";
v0(obj) = 100;

一旦您接受此设计更改,原始问题的解决方案将变得非常简单,如下所示:

template<size_t...>
struct seq{};

template<size_t M, size_t ...N>
struct genseq  : genseq<M-1,M-1, N...> {};

template<size_t ...N>
struct genseq<0,N...>
{
    typedef seq<N...> type;
};

template <typename ...Args, size_t ...N> 
void call_do_stuff_with(std::tuple<Args...> & tpl, seq<N...>)
{
    do_stuff_with(std::get<N>(tpl)...);
}

template <typename ...Args> 
void process(std::tuple<Args...> & tpl)
{
   const size_t N = sizeof ...(Args);
   call_do_stuff_with(tpl, typename genseq<N>::type());
}

如果可以接受,请告诉我。如果这是不可接受的,我会删除我的答案(如果你觉得这样)。

Live demo!