C++ 几种算法的性能。该算法称为at,以下是我使用GCC 10对select\n\t的测量结果:

C++ 几种算法的性能。该算法称为at,以下是我使用GCC 10对select\n\t的测量结果:,c++,c++11,variadic-templates,C++,C++11,Variadic Templates,有关at算法(也称为std::tuple_element_t)编译时缩减的出色背景信息,请参见和的博客文章。以下是另一种精益C++17方法,它也使用折叠表达式;但通过使用std::enable\u(如果),避免使用特殊类代理: template <typename ...Ts> struct select_last { using type = typename decltype((std::enable_if<true,Ts>{}, ...))::type; };


有关at算法(也称为std::tuple_element_t)编译时缩减的出色背景信息,请参见和的博客文章。

以下是另一种精益C++17方法,它也使用折叠表达式;但通过使用
std::enable\u(如果
),避免使用特殊类代理:

template <typename ...Ts>
struct select_last
{
  using type = typename decltype((std::enable_if<true,Ts>{}, ...))::type;
};

template <typename ...Ts>
using select_last_t = typename select_last<Ts...>::type;

static_assert(std::is_same_v<char, select_last_t<int,double,char>>);

很抱歉,我来晚了一点,但我只是遇到了同样的问题,寻找答案,不喜欢我在这里看到的,并意识到它可以用元组来完成。请参见下面的C++11实现。 注:也可以通过这种方式访问第n种类型的变量模板。(该示例不检查N是否超过可变参数的数量,但是可以使用SFINAE技术(例如启用_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;
}
#包括
#包括
结构A
{
char ch='a';
};
结构B
{
char ch='b';
};
结构C
{
char ch='c';
};
模板
结构变量{
使用TypesTuple=std::tuple;
使用LastType=typename std::tuple\u元素::type;
模板
使用NthType=typename std::tuple\u元素::type;
};
int main(int argc,char*argv[]){
一些变量::LastType l;

std::至少可以,但不一定效率更高……哦,等等。这是你的问题:D@DyPohh!!谢谢。我忘了。我能用这种方法在O(1)实例中选择最后一个参数吗?不,至少不能用这种方法。它需要创建一个(整数的)序列要知道哪种类型是最后一种,我所知道的创建这些序列的最佳算法仍然使用O(logN)实例化深度。正如jrok所指出的,您可以通过预处理器创建模板,但它没有那么灵活,也可能不会更快。在删除
declvalu助手时,我遇到了一些奇怪的不完整类型错误。不知道为什么。在一般情况下,没有人拥有数千个参数集,也没有一组参数所以这可能是一种方法。但另一方面,这完全破坏了可变模板的功能。这和Boost::Tuple或Loki一样可怕Typelists@Manu343726但是它可以通过一个last
select\u last\u helper
通过递归处理变量较大的情况通过这种方式,你可以得到
O(max{1,n-k})
,其中
k
是你处理的小案例的数量。你可以编写一系列宏来生成它。但是,我不确定编译器如何处理解析模板专业化-它可以线性扫描选项,因此是
O(n)
反正是时间。shrugI只是想知道你在这里用元组做什么;)但是现在,你已经复制了OP的代码。你也可以将
标记设置为
本地的
select\u last
以避免污染全局名称空间。有没有理由不使用
std::declval()
而不是
标记()
例如
模板结构选择{using type=decltype((std::declval{},…);};
?@marco6,
std::declval()
返回一个引用类型,您必须切掉该引用。但是如果
T
本身是引用类型,您就会遇到问题。在C++20中,我们将为
标记
使用
std::type_identity
。抱歉,我没有看到您提供了指向该代码的链接。我使用了自己的基准测试,所以这就是区别。我没有看到Idone的代码使用了t次,但我不能100%肯定这是最好的,因为除了对执行工作的类型特征的调用之外,文件应该是相同的,否则编译速度的差异可能是由于
#include
等原因。当然,这些差异应该与较大的实例保持一致,但500不是那么大。@Elliott啊,我明白了。(微观)对这类事情进行基准测试是很困难的,不同的方法会产生不同的答案。尽管如此,我相信我们在底线上是一致的:这种基于多重继承的方法通常比基于递归的方法快,但比经过优化的库慢得多(关于一些著名的库,请参阅on和)。
template< class A, class Args...>
struct select_last< Args ... , A>{  using type = A; }; // but it's not compiled.
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;
};
    #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>{};
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, "!");
}
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;
};
#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)...);
}
template<typename T>
struct tag
{
    using type = T;
};

template<typename... Ts>
struct select_last
{
    // Use a fold-expression to fold the comma operator over the parameter pack.
    using type = typename decltype((tag<Ts>{}, ...))::type;
};
#include <utility>

////////////////////////////////////////////////////////////////////////////////

template<std::size_t n, std::size_t i, class>
struct type_if_equal {
  static_assert(n != i, "see specialization");
// missing `type` typedef by purpose
};

template<std::size_t n, class T>
struct type_if_equal<n, n, T> {
  using type = T;
};

////////////////////////////////////////////////////////////////////////////////

template<std::size_t n, class Is, class... Ts>
struct select_nth;

template<std::size_t n, std::size_t... is, class... Ts>
struct select_nth<n, std::index_sequence<is...>, Ts...>
  : type_if_equal<n, is, Ts>...
{};

template<std::size_t n, class... Ts>
using select_nth_t = typename select_nth<
  n, std::index_sequence_for<Ts...>, Ts...
>::type;

////////////////////////////////////////////////////////////////////////////////

template<class T0, class... Ts>
using select_last_t = select_nth_t<sizeof...(Ts), T0, Ts...>;

////////////////////////////////////////////////////////////////////////////////

int main() {
  using T = select_last_t<int, double, double, long, long, long, int, char>;
  static_assert(std::is_same<T, char>{}, "");
}
template <typename ...Ts>
struct select_last
{
  using type = typename decltype((std::enable_if<true,Ts>{}, ...))::type;
};

template <typename ...Ts>
using select_last_t = typename select_last<Ts...>::type;

static_assert(std::is_same_v<char, select_last_t<int,double,char>>);
// C++20
template <typename ...Ts>
struct select_last
{
  using type = typename decltype((std::type_identity<Ts>{}, ...))::type;
};
#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;
}