9

私の目標は、例えば、

pairs<1,2,3,4>()

戻り型あり

std::tuple<some_other_type<1,2>, some_other_type<2,3>, some_other_type<3,4>>

これが C++ テンプレート メタプログラミングで可能であるかどうか、またどのように達成できるか疑問に思っています。実際に値を生成するには、 tuple_cat を使用して再帰的に出力に連結できるように見えますが、それ自体が可変であり、事実上テンプレートパラメーターの数の関数であるため、戻り値の型を表現するのが難しいと感じています。状況を複雑にしているのは、tuple_cat ルートに行った場合、関数をオーバーロードして連結するタプルを取得する必要があり、連結はコンパイル時ではなく実行時に発生することです。私はここで野生のガチョウを追いかけていますか?

4

3 に答える 3

12

ここにそれを行う方法があります。あなたのクラステンプレートを考えるとsome_other_type

template<int I, int J>
struct some_other_type { };

そして、detail名前空間に隠されているいくつかの機械が与えられます:

namespace detail
{
    template<int... Is>
    struct pairs { };

    template<int I, int J>
    struct pairs<I, J> 
    { 
        using type = std::tuple<some_other_type<I, J>>; 
    };

    template<int I, int J, int... Is>
    struct pairs<I, J, Is...>
    {
        using type = decltype(std::tuple_cat(
                std::tuple<some_other_type<I, J>>(),
                typename pairs<J, Is...>::type()));
    };
}

ヘルパー クラス テンプレートをインスタンス化する単純な関数を提供できます。

template<int... Is>
typename detail::pairs<Is...>::type pairs()
{
    return typename detail::pairs<Is...>::type();
}

そして、これを使用する方法(およびテストケース)は次のとおりです。

#include <type_traits>

int main()
{
    auto p = pairs<1, 2, 3, 4>();

    // Won't fire!
    static_assert(
        std::is_same<
            decltype(p),
            std::tuple<
                some_other_type<1,2>,
                some_other_type<2,3>,
                some_other_type<3,4>>
            >::value,
            "Error!");
}

最後に、ライブの例を示します。


改善: (書くことができるのになぜ書く<1, 2, 3, 4><1, 5>のか)?

のテンプレート引数として最小値と最大値の間のすべての数値を手動で書き込む必要がないように、上記のソリューションを拡張することもできますpairs()detail以下の追加の機構を考えると、これも名前空間に隠されています。

namespace detail
{
    template <int... Is>
    struct index_list { };

    template <int MIN, int N, int... Is>
    struct range_builder;

    template <int MIN, int... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    template <int MIN, int N, int... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    { };

    // Meta-function that returns a [MIN, MAX) index range
    template<int MIN, int MAX>
    using index_range = typename range_builder<MIN, MAX>::type;

    template<int... Is>
    auto pairs_range(index_list<Is...>) -> decltype(::pairs<Is...>())
    {
        return ::pairs<Is...>();
    }
}

標準ライブラリのスタイルで、範囲pairs_range()を定義する 2 つのテンプレート引数を受け入れるヘルパー関数を定義することができます。[begin, end)end

template<int I, int J>
auto pairs_range() -> decltype(pairs_range(detail::index_range<I, J>()))
{
    return pairs_range(detail::index_range<I, J>());
}

そして、これはそれを使用する方法です(テストケースを含む):

int main()
{
    // Won't fire!
    static_assert(
        std::is_same<
            decltype(pairs_range<1, 5>()),
            decltype(pairs<1, 2, 3, 4>())
            >::value,
            "Error!");
}

繰り返しになりますが、これは実際の例です。

于 2013-04-01T21:01:43.050 に答える
3

それでは、再帰ありとなしで試してみましょうindices(もちろん、インデックスは除きます)。

#include <tuple>

template< std::size_t... Ns >
struct indices
{
    typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices
{
    typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 >
{
    typedef indices<> type;
};

template< std::size_t, std::size_t >
struct sometype {};

template< typename, typename, typename >
struct make_pairs;

template< std::size_t... Ns, std::size_t... Ms, std::size_t... Is >
struct make_pairs< indices< Ns... >, indices< Ms... >, indices< Is... > >
{
  using type = decltype( std::tuple_cat(
    std::declval< typename std::conditional< Is % 2 == 1,
                                             std::tuple< sometype< Ns, Ms > >,
                                             std::tuple<> >::type >()...
  ));
};

template< std::size_t... Ns >
using pairs = typename make_pairs< indices< 0, Ns... >, indices< Ns..., 0 >,
                typename make_indices< sizeof...( Ns ) + 1 >::type >::type;

int main()
{
  static_assert( std::is_same< pairs<1,2,4,3,5,9>,
    std::tuple< sometype<1,2>, sometype<4,3>, sometype<5,9> > >::value, "Oops" );
}

(わかりました、私は少しごまかしました:std::tuple_catそれ自体が再帰的かもしれません;)


更新:わかりました、質問をもっと注意深く読むべきでした。目的の結果を生成するバージョンは次のとおりです (上記のindices/ make_indices)。

template< std::size_t, std::size_t >
struct sometype {};

template< typename, typename, typename >
struct make_pairs;

template< std::size_t... Ns, std::size_t... Ms, std::size_t... Is >
struct make_pairs< indices< Ns... >, indices< Ms... >, indices< Is... > >
{
  using type = decltype( std::tuple_cat(
    std::declval< typename std::conditional< Is != 0 && Is != sizeof...( Is ) - 1,
                                             std::tuple< sometype< Ns, Ms > >,
                                             std::tuple<> >::type >()...
  ));
};

template< std::size_t... Ns >
using pairs = typename make_pairs< indices< 0, Ns... >, indices< Ns..., 0 >,
                typename make_indices< sizeof...( Ns ) + 1 >::type >::type;

int main()
{
  static_assert( std::is_same< pairs<1,2,3,4>,
    std::tuple< sometype<1,2>, sometype<2,3>, sometype<3,4> > >::value, "Oops" );
}
于 2013-04-01T22:10:53.927 に答える