22

可変個引数テンプレートの最初のパラメーターを選択する方法を知っています:

template< class...Args> struct select_first;
template< class A, class ...Args> struct select_first<A,Args...>{  using type = A;};

とてもシンプルです。ただし、select_last は似ていません。

template< class ...Args> struct select_last;
template< class A> struct select_last<A> { using type = A; };
template< class A, class Args...> struct select_last<A,Args...>{ 
        using type = typename select_last<Args...>::type;
};

このソリューションには、深い再帰的なテンプレートのインスタンス化が必要でした。私はこれを次のように解決しようとします:

template< class A, class Args...>
struct select_last< Args ... , A>{  using type = A; }; // but it's not compiled.

Q: 可変個引数テンプレートの最後のパラメーターを選択するより効果的な方法はありますか?

4

8 に答える 8

8

前回と同じアプローチ、O(logN) インスタンス化の深さ。オーバーロードを1 つだけ使用するため、消費するリソースが少なくなります。

警告: 現在、タプル型から参照を削除しています。 : から参照を削除しましたpack::declval。どんな場面でも今でも通用すると思います。

Xeo によるO(log(N)) インスタンス化のインデックス トリック。std::size_tの代わりに使用するように変更unsigned

    #include <cstddef>

    // using aliases for cleaner syntax
    template<class T> using Invoke = typename T::type;

    template<std::size_t...> struct seq{ using type = seq; };

    template<class S1, class S2> struct concat;

    template<std::size_t... I1, std::size_t... I2>
    struct concat<seq<I1...>, seq<I2...>>
      : seq<I1..., (sizeof...(I1)+I2)...>{};

    template<class S1, class S2>
    using Concat = Invoke<concat<S1, S2>>;

    template<std::size_t N> struct gen_seq;
    template<std::size_t N> using GenSeq = Invoke<gen_seq<N>>;

    template<std::size_t N>
    struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};

    template<> struct gen_seq<0> : seq<>{};
    template<> struct gen_seq<1> : seq<0>{};

今日、タプルの n 番目のタイプ (基本的には の実装std::tuple_element) を取得するための、別の、より単純で、おそらくより高速な (コンパイル時間) ソリューションがあることに気付きました。別の質問の直接的な解決策ですが、完全を期すためにここにも投稿します。

namespace detail
{
    template<std::size_t>
    struct Any
    {
        template<class T> Any(T&&) {}
    };

    template<typename T>
    struct wrapper {};

    template<std::size_t... Is>
    struct get_nth_helper
    {
        template<typename T>
        static T deduce(Any<Is>..., wrapper<T>, ...);
    };

    template<std::size_t... Is, typename... Ts>
    auto deduce_seq(seq<Is...>, wrapper<Ts>... pp)
    -> decltype( get_nth_helper<Is...>::deduce(pp...) );
}

#include <tuple>

template<std::size_t n, class Tuple>
struct tuple_element;

template<std::size_t n, class... Ts>
struct tuple_element<n, std::tuple<Ts...>>
{
    using type = decltype( detail::deduce_seq(gen_seq<n>{},
                                              detail::wrapper<Ts>()...) );
};

最後の要素のヘルパー:

template<typename Tuple>
struct tuple_last_element;

template<typename... Ts>
struct tuple_last_element<std::tuple<Ts...>>
{
    using type = typename tuple_element<sizeof...(Ts)-1,
                                        std::tuple<Ts...>> :: type;
};

使用例:

#include <iostream>
#include <type_traits>
int main()
{
    std::tuple<int, bool, char const&> t{42, true, 'c'};

    tuple_last_element<decltype(t)>::type x = 'c'; // it's a reference

    static_assert(std::is_same<decltype(x), char const&>{}, "!");
}

元のバージョン:

#include <tuple>
#include <type_traits>

namespace detail
{
    template<typename Seq, typename... TT>
    struct get_last_helper;

    template<std::size_t... II, typename... TT>
    struct get_last_helper< seq<II...>, TT... >
    {
        template<std::size_t I, std::size_t L, typename T>
        struct pack {};
        template<typename T, std::size_t L>
        struct pack<L, L, T>
        {
            T declval();
        };

        // this needs simplification..
        template<typename... TTpacked>
        struct exp : TTpacked...
        {
            static auto declval_helper()
                -> decltype(std::declval<exp>().declval());
            using type = decltype(declval_helper());
        };

        using type = typename exp<pack<II, sizeof...(TT)-1, TT>...>::type;
    };
}

template< typename Tuple >
struct get_last;

template< typename... TT >
struct get_last<std::tuple<TT...>>
{
    template<std::size_t... II>
    static seq<II...> helper(seq<II...>);
    using seq_t = decltype(helper(gen_seq<sizeof...(TT)>()));

    using type = typename detail::get_last_helper<seq_t, TT...>::type;
};


int main()
{
    using test_type = std::tuple<int, double, bool, char>;

    static_assert(std::is_same<char, get_last<test_type>::type>::value, "!");
    // fails:
    static_assert(std::is_same<int, get_last<test_type>::type>::value, "!");
}
于 2013-09-22T10:10:17.230 に答える
5

参照を型リストからやみくもに削除したい場合 (これは非常によくあることです: それらが参照であることを知っているか、気にしないかのどちらかです) std、. 基本的にデータをtupleまたはに詰め込み、最後の要素を抽出するためにtie使用します。std::get<sizeof...(X)-1>( tuple or tie )

std::declval< std::tuple<Args...> >()and decltype、場合によってはを使用して、純粋型のコンテキストでこれを行うことができますstd::remove_reference

例として、さまざまな引数のセットがあり、残りを無視して最後の引数を返したいとします。

#define RETURNS(x) ->decltype(x) { return (x); }

template<typename ...Args>
auto get_last( Args&&... args )
  RETURNS( std::get< sizeof...(Args)-1 >( std::tie(std::forward<Args>(args)...) ) )

これを別の関数で使用できます。

template<typename ...Args>
void foo( Args&&... args ) {
  auto&& last = get_last(std::forward<Args>(args)...);
}
于 2013-09-22T14:41:11.250 に答える
1
template <class... Args>
struct select_last;

template <typename T>
struct select_last<T>
{
     using type = T;
};

template <class T, class... Args>
struct select_last<T, Args...>
{
    using type = typename select_last<Args...>::type;
};
于 2013-09-22T12:05:49.677 に答える
1

パーティーに少し遅れて申し訳ありませんが、同じ問題に遭遇し、答えを探し、ここにあるものが気に入らず、タプルを使用して実行できることに気付きました。以下の C++11 実装を参照してください。注: この方法で可変個引数テンプレートの N 番目の型にアクセスすることもできます。(この例では、N が可変引数の数を超えていることを確認していませんが、たとえば、SFINAE 手法 (enable_if) を使用して確認できます) それは受け入れられる答えですか、それとも質問に何か不足していますか?

#include <tuple>
#include <iostream>

struct A
{
    char ch = 'a';
};
struct B
{
    char ch = 'b';
};
struct C
{
    char ch = 'c';
};


template <typename... Types>
struct SomeVariadic {

    using TypesTuple = std::tuple<Types...>;

    using LastType = typename std::tuple_element<sizeof...(Types)-1, TypesTuple>::type;

    template <int N>
    using NthType = typename std::tuple_element<N, TypesTuple>::type;
};



int main(int argc, char* argv[]) {

    SomeVariadic<A,B,C>::LastType l;

    std::cout << SomeVariadic<A,B,C>::LastType().ch << " "
            << SomeVariadic<A,B,C>::NthType<1>().ch<< std::endl;
}
于 2019-06-04T16:19:01.723 に答える
-3

かなりばかげたアプローチは、ヘルパー クラスを作成し、パラメーターの数ごとに特化することです (選択した制限まで)。そのためにプリプロセッサを使用できます。

template<typename...>
struct select_last_helper;

template<typename T1>
struct select_last_helper<T1> {
    using type = T1;
};

template<typename T1, typename T2>
struct select_last_helper<T1,T2> {
    using type = T2;
};

template<typename T1, typename T2, typename T3>
struct select_last_helper<T1,T2,T3> {
    using type = T3;
};

template<typename... Ts>
struct select_last {
    using type = typename select_last_helper<Ts...>::type;
};

O(1) テンプレートのインスタンス化 :)

于 2013-09-22T09:52:31.393 に答える