我知道如何选择可变参数模板的第一个参数:
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;
};
此解决方案需要深度递归模板即时功能。 我试着用as:
解决这个问题template< class A, class Args...>
struct select_last< Args ... , A>{ using type = A; }; // but it's not compiled.
问:存在更有效的方法来选择可变参数模板的最后一个参数吗?
答案 0 :(得分:13)
使用C ++ 17,最干净的方法是
template<typename T>
struct tag
{
using type = T;
};
template<typename... Ts>
struct select_last
{
using type = typename decltype((tag<Ts>{}, ...))::type;
};
具有O(1)实例化深度。
答案 1 :(得分:8)
与上次相同的方法,O(logN)实例化深度。仅使用一个重载,因此它应该消耗更少的资源。
警告:它当前从元组类型中删除引用。
注意:从pack::declval
删除了引用。我认为它仍适用于所有情况。
indices trick in O(log(N)) instantiations;修改为使用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
的实现)。即使它是another question的直接解决方案,我也会在此处发布以便完整。
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, "!");
}
答案 2 :(得分:4)
如果您愿意盲目地从类型列表中删除引用(通常情况如此:要么您知道它们是引用,要么您不关心),您可以使用{{1之外的少量机器来执行此操作}}。基本上将数据填入std
或tuple
,然后使用tie
提取最后一个元素。
您可以使用std::get<sizeof...(X)-1>( tuple or tie )
和std::declval< std::tuple<Args...> >()
以及可能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)...) ) )
答案 3 :(得分:3)
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;
};
答案 4 :(得分:0)
如果C ++ 17可用并且只对最后一种类型感兴趣,那么other solution就很棒了。
如果需要C ++ 14支持(C ++ 11加index_sequence
)或者如果一个人对第n种类感兴趣,那么一个好的解决方案是
#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>{}, "");
return 0;
}
答案 5 :(得分:0)
以下是另一种精益C ++ 17方法,该方法也使用 fold-expression ;但使用std::enable_if
可以避免临时类代理:
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中,std::type_identity
提供了一种更具可读性的方法:
// C++20
template <typename ...Ts>
struct select_last
{
using type = typename decltype((std::type_identity<Ts>{}, ...))::type;
};
答案 6 :(得分:0)
很抱歉参加聚会晚了一点,但是我遇到了同样的问题,寻找答案,不喜欢我在这里看到的东西,意识到可以使用元组来完成。请参阅下面的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;
}
答案 7 :(得分:-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)模板实例化:)