2016-04-24 16 views
1

私はmap_nテンプレートを持っています。これは、N個の入力タプルから要素の各セットにN-arity関数を適用して新しい出力タプルを生成します。すべての入力タプルは同じ長さでなければなりません(静的なアサーションで確認する必要があります)。非形式引数のC++バリデーションテンプレート部分特殊化

次のコードスニペットに示すように、再帰終了条件の部分的な特殊化を一般的な方法で書くことができなかったことを除いて、コードは正しく動作します。

#include <tuple> 
#include <cassert> 

namespace impl { 
    // car, cdr, cons implementation 
    // 
    template<unsigned... XS> 
    struct sequence { 
     template<unsigned X> 
     using cons = sequence<X, XS...>; 
    }; 

    template<unsigned start, unsigned end> 
    struct range { 
     static_assert(start < end, "Range: start > end"); 
     using type = typename range<start + 1, end>::type::template cons<start>; 
    }; 

    template<unsigned start> 
    struct range<start, start> { 
     using type = sequence<>; 
    }; 

    template<typename T, unsigned... N> 
    auto select(const T& t, sequence<N...>) { 
     return std::make_tuple(std::get<N>(t)...); 
    } 

} // end namespace impl 

// car, cdr, cons 
// 
// empty list 
// 
constexpr const std::tuple<> empty; 

// car 
// 
template<typename T> 
auto car(const T& t) { return std::get<0>(t); } 

// cdr 
// 
template<typename T, typename R = typename impl::range<1, std::tuple_size<T>::value>::type> 
auto cdr(const T& t) { 
    return impl::select(t, R()); 
} 

// cons 
// 
template<typename X, typename... XS> 
auto cons(X x, const std::tuple<XS...>& t) { 
    return std::tuple_cat(std::make_tuple(x), t); 
} 

namespace impl { 
    // map_n implementation 
    template<typename F, typename... Ts> 
    struct map_n_impl { 
     static auto map(const F& f, const Ts&... t) { 
      return cons(
       f(car(t)...), 
       map_n_impl<F, decltype(cdr(t))...>::map(f, cdr(t)...) 
       ); 
      } 
     }; 

    // NOTE: Need a more general specialization here 
    // 
    template<typename F> 
    struct map_n_impl<F, std::tuple<>, std::tuple<>> { 
     static std::tuple<> map(const F&, const std::tuple<>&, const std::tuple<>&) 
     { 
      return std::make_tuple(); 
     } 
    }; 
} // end namespace impl 

// map_n 
// 
template<typename F, typename... Ts> 
auto map_n(const F& f, const Ts&... t) { 
    return impl::map_n_impl<F, Ts...>::map(f, t...); 
} 


int main(int, const char **) { 
    { 
     auto tup1 = std::make_tuple(1.0, 2.0, 3.0); 
     auto tup2 = std::make_tuple(0.0, 1.0, 2.0); 
     auto r = map_n([](auto x, auto y) { return x - y; }, tup1, tup2); 
     assert(std::get<0>(r) == 1.0); 
     assert(std::get<1>(r) == 1.0); 
     assert(std::get<2>(r) == 1.0); 
    } 

    // { 
    // auto tup1 = std::make_tuple(1.0, 2.0, 3.0); 
    // auto tup2 = std::make_tuple(0.0, 1.0, 2.0); 
    // auto tup3 = std::make_tuple(4.0, 5.0, 6.0); 
    // auto r = map_n([](auto x, auto y, auto z) { return x - y + z; }, tup1, tup2, tupe3); 
    // assert(std::get<0>(r) == 5.0); 
    // assert(std::get<1>(r) == 6.0); 
    // assert(std::get<2>(r) == 7.0); 
    // } 

    return 0; 
} 

答えて

1

あなたの予定よりもはるかに簡単です。 map_n_implはまったく必要ありません。機能的な再帰的アプローチを採用する場合、map_nの2つのオーバーロードが必要です。すべてのタプルが空でなく、すべてのタプルが空です。 「

template<typename F, typename... Ts, 
    std::enable_if_t<!all_empty<Ts...>::value, int*> = nullptr> 
auto map_n(const F& f, const Ts&... t) { 
    return cons(
     f(car(t)...), 
     map_n(f, cdr(t)...) 
     ); 
} 

template<typename F, typename... Ts, 
    std::enable_if_t<all_empty<Ts...>::value, int*> = nullptr> 
auto map_n(const F& , const Ts&... t) { 
    return std::make_tuple(t...); 
} 

なおtuple ISN:

template <bool... > 
struct bool_pack; 

template <bool... b> 
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>; 

template <class... T> 
using all_empty = all_true<std::is_same<T, std::tuple<>>::value...>; 

そしてちょうどSFINAEにそれを使用する2つの互いに素条件:我々は、彼らがすべての空のかいないのであれば把握するコロンボのbool_packトリックを使用します実際に行うべき最善の方法はcons/car/cdrで、C++ではそれほど役に立ちません。cdr-ableではありません。より適切なものは、pairの入れ子になります。

また、tuple全体を索引順序トリックで一度に構築することもできます。 2つのパラメータパックを別々に解凍する必要があるので、ここではちょっと迷惑でしょう。したがって、余分なcallラムダ。これを行うには、より良い方法があるかもしれません。

template <size_t... Is, class F, class... Ts> 
auto map_n_impl(std::index_sequence<Is...>, const F& f, const Ts&... ts) { 
    auto call = [&](auto idx){ 
     return f(std::get<idx>(ts)...); 
    }; 

    return std::make_tuple(
     call(std::integral_constant<size_t, Is>{})... 
     ); 
} 

template <class F, class T0, class... Ts> 
auto map_n(const F& f, const T0& t0, const Ts&... ts) { 
    return map_n_impl(
     std::make_index_sequence<std::tuple_size<T0>::value>{}, 
     f, 
     t0, 
     ts...); 
} 
+0

's/decltype(idx):: value/idx /'です。 –

+0

@ T.C。それが可能であることを認識していませんでした。クール。 – Barry

関連する問題