9

我有一个元组类型。我想在其中添加一个元素类型以获得一个新的元组类型。我可以这样做

decltype tuple_cat(MyTuple, std::tuple<MyType>())

但是,我没有找到tuple_catboost::tuple如何在 boost 中做到这一点?

4

2 回答 2

7

我假设您希望在编译时完成所有这些。

这是一般的解释:连接元组类似于连接列表或数组,只是算法是相同的。在这里,给定元组ab,我选择将 的最后一个元素移动a到 的开头b,并重复直到a为空。

第一:基础结构。以下结构保留了一个参数包。它可以是任何东西,例如一个元组:

template<typename... T>
struct pack
{
    static const unsigned int size = sizeof...(T);
};

请注意,包的大小存储在其中。它不是强制性的,但便于解释。Boost 使用结构boost::tuples::length<T>::value(更详细)。

要访问第 i 个位置的元素,我们使用类似于以下的结构boost::tuples::element<n, T>

// Get i-th element of parameter pack
// AKA 'implementation'
// Principle: the element i is the first element of the sub-array starting at indice i-1
template<int n, typename F, typename... T>
struct element_at : public element_at<n-1, T...>
{
};

template<typename F, typename... T>
struct element_at<0, F, T...>
{
    typedef F type;
};

// Get i-th element of pack
// AKA 'interface' for the 'pack' structure
template<int n, typename P>
struct element
{
};

template<int n, typename... T>
struct element<n, pack<T...>>
{
    typedef typename element_at<n, T...>::type type;
};

现在,我们必须使用一个低级操作,将一个元素添加到包的一侧(在左侧或右侧添加)。这里选择了在左边添加,但它不是唯一的选择:

// Concat at left (only for structure 'pack')
template<typename a, typename b>
struct tuple_concat_left
{
};

template<typename a, typename... b>
struct tuple_concat_left<a, pack<b...>>
{
    typedef pack<a, b...> type;
};

对于模板,a没有更改,而是使用索引来了解要添加的元素。继承定义了一个“类型”类型定义,它是所有索引之后n和另一个元组(不包括n, 和按顺序)的串联。我们只需要在左侧连接 indice 的元素n

// Concat 2 tuples
template<typename a, typename b, int n = 0, bool ok = (n < a::size)>
struct tuple_concat : public tuple_concat<a, b, n+1>
{
    typedef typename tuple_concat_left<
        typename element<n, a>::type,
        typename tuple_concat<a, b, n+1>::type
    >::type type;
};

template<typename a, typename b, int n>
struct tuple_concat<a, b, n, false>
{
    typedef b type;
};

就是这样!现场示例在这里

现在,对于元组细节:您注意到我没有使用 boost::tuple 或 std::tuple。那是因为很多 boost 元组的实现都无法访问可变参数模板,所以使用了固定数量的模板参数(它们默认为boost::tuples::null_type)。将其直接与可变参数模板一起使用是一件令人头疼的事情,因此需要有另一个抽象。

我还假设您可以使用 C++11(decltype在您的问题中)。在 C++03 中连接 2 个元组是可能的,但更重复和无聊。

您可以pack非常轻松地将 a 转换为元组:只需将pack定义更改为:

template<typename... T>
struct pack
{
    static const unsigned int size = sizeof...(T);
    typedef boost::tuple<T...> to_tuple; // < convert this pack to a boost::tuple
};
于 2013-07-14T22:03:17.453 回答
1

C++14 提供了一个库来生成编译类型的整数序列。这有助于操作像元组和数组这样的静态序列(示例)。可以得到一个整数序列

template<size_t... Ints>
struct integer_sequence {};

template<size_t Size, size_t... Ints>
struct implementation : implementation<Size-1, Size-1, Ints...> {};

template<size_t... Ints>
struct implementation<0, Ints...>
{
    typedef integer_sequence<Ints...> type;
};

template<class... T>
using index_sequence_for = typename implementation<sizeof...(T)>::type;

要连接MyTupleMyType您可以编写简单的函数:

template<typename X, typename Tuple, size_t... Ints>
auto concat(X x, Tuple t, integer_sequence<Ints...>)
    -> decltype( std::make_tuple(x, std::get<Ints>(t)...) )
{
    return std::make_tuple(x, std::get<Ints>(t)...);
}

template<typename X, typename... T>
std::tuple<X, T...> concat(X x, std::tuple<T...> t)
{
    return concat(x, t, index_sequence_for<T...>());
}

concat(MyType, MyTuple);
于 2013-07-14T22:40:40.663 回答