如何将可变参数模板参数分成两部分?例如:拆分可变参数模板参数
template <int d> struct a {
std::array <int, d> p, q;
template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {}
};
如何将可变参数模板参数分成两部分?例如:拆分可变参数模板参数
template <int d> struct a {
std::array <int, d> p, q;
template <typename ... T> a (T ... t) : p ({half of t...}), q ({other half of t...}) {}
};
我们仍然缺乏操纵可变参数包(或者我不知道它们)的很多帮助。直到一个好的Boost库将它们带给我们,我们仍然可以编写我们自己的。
例如,如果你愿意你的阵列初始化推迟到构造体,您可以创建和使用fonction的参数包拷贝一部分到输出迭代器:
#include <array>
#include <cassert>
#include <iostream>
// Copy n values from the parameter pack to an output iterator
template < typename OutputIterator >
void copy_n(size_t n, OutputIterator)
{
assert (n == 0);
}
template < typename OutputIterator, typename T, typename... Args >
void copy_n(size_t n, OutputIterator out, const T & value, Args... args)
{
if (n > 0)
{
*out = value;
copy_n(n - 1, ++out, args...);
}
}
// Copy n values from the parameter pack to an output iterator, starting at
// the "beginth" element
template < typename OutputIterator >
void copy_range(size_t begin, size_t size, OutputIterator out)
{
assert(size == 0);
}
template < typename OutputIterator, typename T, typename... Args >
void copy_range(size_t begin, size_t size, OutputIterator out, T value, Args... args)
{
if (begin == 0)
{
copy_n(size, out, value, args...);
}
else
{
copy_range(begin - 1, size, out, args...);
}
}
template < int N >
struct DoubleArray
{
std::array< int, N > p;
std::array< int, N > q;
template < typename... Args >
DoubleArray (Args... args)
{
copy_range(0, N, p.begin(), args...);
copy_range(N, N, q.begin(), args...);
}
};
int main()
{
DoubleArray<3> mya(1, 2, 3, 4, 5, 6);
std::cout << mya.p[0] << mya.p[2] << std::endl;
std::cout << mya.q[0] << mya.q[2] << std::endl;
}
// Ouput:
// 13
// 46
,你可以看,你可以(不那么容易)创建你自己的算法来操作参数包;所有需要的是对递归和模式匹配的一个很好的理解(就像在进行Template MetaProgramming时一样)。
这样的增强库已经存在:boost.fusion。如果将参数包转换为一个'tuple',boost.fusion可以用来将'tuple'拆分成两个单独的'tuple'或'boost :: fusion :: vector's。我编写了一个演示代码,但目前我只有VC++方便,缺乏可变参数模板。 – ildjarn 2011-03-30 18:44:51
我知道Boost.Fusion库,但是我正在考虑处理参数包的递归性质的库:我们应该能够操作参数包,而不首先将它转换为元组,这需要进行不必要的计算。不过,我确实可以通过使用Fusion来简化代码(也许我会发布另一个答案来说明如何完成这项工作)。 – 2011-03-31 07:29:17
Luc的解决方案简洁明了,但缺乏乐趣。
因为只有一个使用可变参数模板有道,这是滥用他们做疯狂的过于复杂的元编程的东西:)
像这样:
template <class T, size_t... Indx, class... Ts>
std::array<T, sizeof...(Indx)>
split_array_range_imp(pack_indices<Indx...> pi, Ts... ts)
{
return std::array<T, sizeof...(Indx)>{get<Indx>(ts...)...}; //TADA
}
template <class T, size_t begin, size_t end, class... Ts>
std::array<T, end - begin>
split_array_range(Ts... ts)
{
typename make_pack_indices<end, begin>::type indices;
return split_array_range_imp<T>(indices, ts...);
}
template <size_t N>
struct DoubleArray
{
std::array <int, N> p, q;
template <typename ... Ts>
DoubleArray (Ts ... ts) :
p(split_array_range<int, 0 , sizeof...(Ts)/2 >(ts...)),
q(split_array_range<int, sizeof...(Ts)/2, sizeof...(Ts) >(ts...))
{
}
};
int main()
{
DoubleArray<3> mya{1, 2, 3, 4, 5, 6};
std::cout << mya.p[0] << "\n" << mya.p[1] << "\n" << mya.p[2] << std::endl;
std::cout << mya.q[0] << "\n" << mya.q[1] << "\n" << mya.q[2] << std::endl;
}
这是很短,但我们需要来编码一些帮助程序:
首先,我们需要make_pack_indices结构,它用于在编译时生成一个整数范围。例如make_pack_indices<5, 0>::type
实际上是类型pack_indices<0, 1, 2, 3, 4>
template <size_t...>
struct pack_indices {};
template <size_t Sp, class IntPack, size_t Ep>
struct make_indices_imp;
template <size_t Sp, size_t ... Indices, size_t Ep>
struct make_indices_imp<Sp, pack_indices<Indices...>, Ep>
{
typedef typename make_indices_imp<Sp+1, pack_indices<Indices..., Sp>, Ep>::type type;
};
template <size_t Ep, size_t ... Indices>
struct make_indices_imp<Ep, pack_indices<Indices...>, Ep>
{
typedef pack_indices<Indices...> type;
};
template <size_t Ep, size_t Sp = 0>
struct make_pack_indices
{
static_assert(Sp <= Ep, "__make_tuple_indices input error");
typedef typename make_indices_imp<Sp, pack_indices<>, Ep>::type type;
};
我们还需要一个get()函数,非常相似到std ::得到的元组,如std::get<N>(ts...)
回报参数组的第N个元素。
template <class R, size_t Ip, size_t Ij, class... Tp>
struct Get_impl
{
static R& dispatch(Tp...);
};
template<class R, size_t Ip, size_t Jp, class Head, class... Tp>
struct Get_impl<R, Ip, Jp, Head, Tp...>
{
static R& dispatch(Head& h, Tp&... tps)
{
return Get_impl<R, Ip, Jp + 1, Tp...>::dispatch(tps...);
}
};
template<size_t Ip, class Head, class... Tp>
struct Get_impl<Head, Ip, Ip, Head, Tp...>
{
static Head& dispatch(Head& h, Tp&... tps)
{
return h;
}
};
template <size_t Ip, class ... Tp>
typename pack_element<Ip, Tp...>::type&
get(Tp&... tps)
{
return Get_impl<typename pack_element<Ip, Tp...>::type, Ip, 0, Tp...>::dispatch(tps...);
}
但建立的get(),我们还需要一个帮手pack_element结构,又非常相似到std :: tuple_element,如pack_element<N, Ts...>::type
是第N类型的参数组。
template <size_t _Ip, class _Tp>
class pack_element_imp;
template <class ..._Tp>
struct pack_types {};
template <size_t Ip>
class pack_element_imp<Ip, pack_types<> >
{
public:
static_assert(Ip == 0, "tuple_element index out of range");
static_assert(Ip != 0, "tuple_element index out of range");
};
template <class Hp, class ...Tp>
class pack_element_imp<0, pack_types<Hp, Tp...> >
{
public:
typedef Hp type;
};
template <size_t Ip, class Hp, class ...Tp>
class pack_element_imp<Ip, pack_types<Hp, Tp...> >
{
public:
typedef typename pack_element_imp<Ip-1, pack_types<Tp...> >::type type;
};
template <size_t Ip, class ...Tp>
class pack_element
{
public:
typedef typename pack_element_imp<Ip, pack_types<Tp...> >::type type;
};
然后我们走了。
其实我不明白为什么pack_element和get()都不在标准库中。那些助手是为std :: tuple存在的,为什么不用参数包?
注意:我对pack_element和make_pack_indices的实现是在libC++中找到的std :: tuple_element和__make_tuple_indices实现的直接转换。
我认为这是最干净的解决方案。我看的越多,我越喜欢它。 – Thomas 2011-04-01 21:12:54
海事组织这应该是被接受的答案,因为这是正确的做法。在运行时复制元素具有相同的效果,但带来的性能损失完全可以避免。 – 2012-07-16 20:28:15
使用const引用而不是引用更好吗?在哪些情况下会使用参考文献?我编译整个事情使用const引用,而不是它工作正常。 – prestokeys 2014-06-17 23:24:43
这里又是另一种解决方案:
#include <array>
#include <tuple>
#include <iostream>
template <int i, int o> struct cpyarr_ {
template < typename T, typename L > static void f (T const& t, L &l) {
l[i-1] = std::get<i-1+o> (t);
cpyarr_<i-1,o>::f (t,l);
}
};
template <int o> struct cpyarr_ <0,o> {
template < typename T, typename L > static void f (T const&, L&) {}
};
template <int i, int o, typename U, typename ... T> std::array < U, i > cpyarr (U u, T... t) {
std::tuple < U, T... > l { u, t... };
std::array < U, i > a;
cpyarr_<i,o>::f (l, a); // because std::copy uses call to memmov which is not optimized away (at least with g++ 4.6)
return a;
}
template <int d> struct a {
std::array <int, d> p, q;
template <typename ... T> a (T ... t) : p (cpyarr<d,0> (t...)), q (cpyarr<d,d> (t...)) {}
};
int main() {
a <5> x { 0,1,2,3,4,5,6,7,8,9 };
for (int i = 0; i < 5; i++)
std::cout << x.p[i] << " " << x.q[i] << "\n";
}
我知道这个问题是很老,但我发现它只是昨天在寻找一个解决一个非常类似的问题。我自己制定了一个解决方案,最后写了一个小型的图书馆,我相信这是你想要的。如果你还有兴趣,你可以找到描述here。
注意,在这种特殊情况下,你可以使用std::initializer_list
:
template<int... Is> struct index_sequence{};
template<int N, int... Is> struct make_index_sequence
{
typedef typename make_index_sequence<N - 1, N - 1, Is...>::type type;
};
template<int... Is> struct make_index_sequence<0, Is...>
{
typedef index_sequence<Is...> type;
};
template <int d> struct a {
std::array <int, d> p, q;
constexpr a (const std::initializer_list<int>& t) :
a(t, typename make_index_sequence<d>::type())
{}
private:
template <int... Is>
constexpr a(const std::initializer_list<int>& t, index_sequence<Is...>) :
p ({{(*(t.begin() + Is))...}}),
q ({{(*(t.begin() + d + Is))...}})
{}
};
有趣的问题。我想,因为你可能可以将参数拆分为元组,所以可以使用http://ideone.com/YyeNC作为开始。令人遗憾的是,构造函数的多个参数包是非法的,即使在这种情况下,它应该清楚包中的类型是什么。 – visitor 2011-03-30 11:57:09