3

一般形の与えられた 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-。同様に重要なのは、本体内で使用されていないメンバーを持たないことです。v-

このプログラムは 100% 自己完結型でなければならないため、Boost は使用しないでください。C++11 の機能は問題ありません。

4

2 に答える 2

3

has_v0andなどの特性のセットを記述しhas_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;

例えば

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);
}
于 2013-01-08T19:07:50.780 に答える
2

@Jonathan Wakely によって 1 つの解決策が提供され、has_XXXメタ関数の使用が採用されています。

ここに別の解決策がありますが、完全な構造体定義を の単なる typedef に変更する必要がありますstd::tuple<>

本格的な構造体:

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());
}

それが受け入れられるかどうか教えてください。それが受け入れられない場合は、回答を削除します (そう思われる場合)。

ライブデモ!

于 2013-01-08T20:07:10.537 に答える