为什么没有分段元组构造?

时间:2012-08-07 13:01:48

标签: c++ c++11 tuples piecewise

标准模板std::pairstd::arraystd::tuple的特例,理所当然,它们应该具有非常相似的功能集。

但是,在这三者之间唯一,std::pair允许分段构造。也就是说,如果类型T1T2可以从一组参数a1, a2, ...b1, b2, ...构建,那么从道德上讲,我们可以成对一对

"pair<T1, T2> p(a1, a2, ..., b1, b2, ...)"

直接。实际上,这个拼写是这样的:

std::pair<T1, T2> p(std::piecewise_construct,
                    std::forward_as_tuple(a1, a2, ...),
                    std::forward_as_tuple(b1, b2, ...));

问题:为什么阵列和元组不存在相同的分段构造性?有一个深刻的原因,还是一个普通的遗漏?例如,拥有:

会很高兴
std::tuple<T1, T2, T3> t(std::piecewise_construct,
                         std::forward_as_tuple(a1, a2, ...),
                         std::forward_as_tuple(b1, b2, ...),
                         std::forward_as_tuple(c1, c2, ...));

有没有理由不能这样做? [编辑:或者我是否完全误解了分段建构的目的?]

(我确实有这样一种情况,我想初始化一个带有默认元素值的元组向量,我宁愿直接从参数构造,而不是再次拼出每个元组元素类型。)

3 个答案:

答案 0 :(得分:15)

  

问题:为什么数组和元组不存在相同的分段构造性?

我的回忆是分段构造被添加到std::pair只是出于一个原因:支持对元素的use-allocator构造,即允许提供分配器并且如果它们支持则有条件地传递给元素使用分配器构建(参见标准中的[allocator.uses])。

在C ++ 0x进程中的某个时刻std::pair的构造函数是现在的两倍,每个构造函数都有一个相应的“allocator-extended”版本,它带有std::allocator_arg_t和一个分配器论证例如

template<class T, class U>
  struct pair {
    pair();
    pair(allocator_arg_t, const Alloc&);
    template<class TT, class UU>
      pair(TT&&, UU&&);
    template<class Alloc, class TT, class UU>
      pair(allocator_arg_t, const Alloc&, TT&&, UU&&);
    // etc.
对于std::pair的疯狂复杂性,有一些正在流行的玩笑(哈哈,只是严肃的)。将分配器传递给元素的支持已从std::pair中删除并移至std::scoped_allocator_adaptorconstruct负责检测是否应使用分配器构造元素(请参阅std::pair重载指向[allocator.adaptor.members]中tuple的指针。

分段构造的一个很好的结果是你可以对对元素进行“emplace”样式初始化,允许成对的不可移动,不可复制的类型,但据我所知,这不是设计的目标

因此pair不支持它的原因是该特性是为了简化tuple而发明的,它从C ++ 03中的一个非常简单的类型膨胀到C ++中的笑柄0x,但对scoped_allocator_adaptor做同样的事情并不重要(无论如何,这对C ++ 11来说都是新的)。此外,扩展std::array以处理任意数量元素的元组会使适配器太多更复杂。

对于piecewise_construct_t,这是一种聚合类型(因为原因),因此如果不将其作为非聚合,则无法添加使用{{1}}的构造函数。

答案 1 :(得分:12)

我不确定为什么它不存在。以前,我认为在给定当前的varadic模板语法的情况下实现是不可能的,但我意识到如果它被分成几部分就可以完成。

如果他们定义了这样的界面:

template<typename... T>
tuple(piecewise_construct, T&&... t);

并且要求参数是可以使用std::get<N>来访问参数(基本上是元组,对,数组)。必须进行额外的检查以验证给定的参数数量和元组中的元素数量之间不存在不匹配。

编辑:自从我读到这个问题以来,这个问题一直困扰着我。我创建了以下类,它派生自std::tuple,并且没有数据成员,因此您可以将其分配给元组,并且切片是无害的。当前版本要求元素可移动或可复制,因为它创建一个临时元素然后将其插入元组。如果你是一个元组实现者,那么应该可以消除这一举动。

namespace detail
{
template<int ... N>
struct index {
    typedef index<N..., sizeof...(N)> next;
};
template<int N>
struct build_index {
    typedef typename build_index<N - 1>::type::next type;
};

template<>
struct build_index<0> {
    typedef index<> type;
};

template<typename T>
struct tuple_index {
    typedef typename build_index<
            std::tuple_size<typename std::remove_reference<T>::type>::value>::type type;

};
}
template<typename ... Elements>
class piecewise_tuple: public std::tuple<Elements...>
{
    typedef std::tuple<Elements...> base_type;

    template<int Index, typename ... Args, int ... N>
    static typename std::tuple_element<Index, base_type>::type 
    construct(std::tuple<Args...>&& args, detail::index<N...>)
    {
        typedef typename std::tuple_element<Index, base_type>::type result_type;
        return result_type(std::get<N>(std::move(args))...);
    }

    template<int ...N, typename ArgTuple>
    piecewise_tuple(detail::index<N...>, ArgTuple&& element_args)
    : base_type( construct<N>( std::get<N>(std::forward<ArgTuple>(element_args)),
                 typename detail::tuple_index< typename std::tuple_element<N, typename std::remove_reference<ArgTuple>::type >::type >::type() )...)
    {

    }

public:

    piecewise_tuple() = default;

    // For non-piecewise constructors, forward them
    template<typename... Args>
    piecewise_tuple(Args&&... args) : base_type(std::forward<Args>(args)...) {}


    template<typename... T>
    piecewise_tuple(std::piecewise_construct_t, T&&... args) :
    piecewise_tuple(typename detail::tuple_index<base_type>::type(),    
                    std::forward_as_tuple(std::forward<T>(args)...))
    {

    }


};

// Usage example
int main()
{
   int i = 5;
   std::unique_ptr<int> up(new int(0));

   piecewise_tuple<std::pair<int, int>, double, std::unique_ptr<int>, int& >
   p(std::piecewise_construct,
    std::forward_as_tuple(1,2),
    std::forward_as_tuple(4.3),
    std::forward_as_tuple(std::move(up)),
    std::forward_as_tuple(i));
   return 0;
}

答案 2 :(得分:0)

这是我对分段元组的实现(它也允许省略omit“keyword”的值。零开销(无复制/移动 - 直接构建):

http://coliru.stacked-crooked.com/a/6b3f9a5f843362e3

#include <tuple>
#include <utility>
#include <typeinfo>


struct Omit{} omit;


template <class Field, class ...Fields>
struct TupleHolder{
    using fieldT = Field;
    using nextT = TupleHolder<Fields...>;

    Field field;
    TupleHolder<Fields...> next;

    TupleHolder(){}

    template <class ...ValuesRef>
    TupleHolder(Omit, ValuesRef&& ... values)
            : next( std::forward<ValuesRef>(values)... )
    {}

    template <std::size_t ...ids, class FieldValue, class ...ValuesRef>
    TupleHolder(std::index_sequence<ids...>, FieldValue&& field, ValuesRef&& ... values)
            :
            field( std::get<ids>(std::forward<FieldValue>(field))... ),
            next( std::forward<ValuesRef>(values)... )

    {};


    template <class FieldValue, class ...ValuesRef>
    TupleHolder(FieldValue&& field, ValuesRef&& ... values)
            : TupleHolder(
            std::make_index_sequence<
                    std::tuple_size< std::decay_t<FieldValue> >::value
            >(),
            std::forward<FieldValue>(field),
            std::forward<ValuesRef>(values)...
    )
    {}

};


template <class Field>
struct TupleHolder<Field>{
    using fieldT = Field;
    Field field;    // actually last

    TupleHolder(){}
    TupleHolder(Omit){}

    template <std::size_t ...ids, class FieldValue>
    TupleHolder(std::index_sequence<ids...>, FieldValue&& field)
            :
            field( std::get<ids>(std::forward<FieldValue>(field))... )
    {}


    template <class FieldValue>
    TupleHolder(FieldValue&& field)
            : TupleHolder(
            std::make_index_sequence<
                    std::tuple_size< std::decay_t<FieldValue> >::value
            >(),
            std::forward<FieldValue>(field)
    )
    {}
};



template <int index, int target_index, class T>
struct GetLoop{
    using type = typename T::nextT;

    constexpr static decltype(auto) get(T& data) noexcept{
        return GetLoop<index+1, target_index, typename T::nextT>::get(
                data.next
        );
    }

    constexpr static decltype(auto) get(const T& data) noexcept{
        return GetLoop<index+1, target_index, typename T::nextT>::get(
                data.next
        );
    }


    constexpr static decltype(auto) get(T&& data) noexcept{
        return GetLoop<index+1, target_index, typename T::nextT>::get(
                std::forward<type>(data.next)
        );
    }
};

template <int target_index, class T>
struct GetLoop<target_index, target_index, T>{
    using type = typename T::fieldT;

    constexpr static type& get(T& data) noexcept{
        return data.field;
    }

    constexpr static const type& get(const T& data) noexcept{
        return data.field;
    }

    constexpr static type&& get(T&& data) noexcept{
        return std::forward<type>(data.field);
    }
};


// ----------------------------------------------------------------------------------
//                          F R O N T E N D
// ----------------------------------------------------------------------------------

template<class ...FieldTypes>
struct TuplePiecewise{
    using fieldsT = TupleHolder<FieldTypes...>;
    TupleHolder<FieldTypes...> data;

    TuplePiecewise(){}

   // allow copy constructor
   TuplePiecewise(TuplePiecewise& other)
            : TuplePiecewise(static_cast<const TuplePiecewise&>(other)) {}


    template <class ...ValuesRef>
    explicit constexpr TuplePiecewise(ValuesRef&& ... values) noexcept
            : data( std::forward<ValuesRef>(values)... ){}

    TuplePiecewise( const TuplePiecewise& other ) = default;
    TuplePiecewise( TuplePiecewise&& other ) = default;


    static constexpr const std::size_t size = sizeof...(FieldTypes);
};


template<int index, class ...FieldTypes>
constexpr decltype(auto) get(TuplePiecewise<FieldTypes...> &&list) noexcept {
    return GetLoop<0, index, typename TuplePiecewise<FieldTypes...>::fieldsT >::get(  std::move(list.data) );
}

template<int index, class ...FieldTypes>
constexpr decltype(auto) get(TuplePiecewise<FieldTypes...> &list) noexcept {
    return GetLoop<0, index, typename TuplePiecewise<FieldTypes...>::fieldsT >::get(  list.data );
}

template<int index, class ...FieldTypes>
constexpr decltype(auto) get(const TuplePiecewise<FieldTypes...> &list) noexcept {
    return GetLoop<0, index, typename TuplePiecewise<FieldTypes...>::fieldsT >::get(  list.data );
}

用法:

TuplePiecewise< CopyTest, int&, string, int >
list (forward_as_tuple(45,63), forward_as_tuple(i), forward_as_tuple("hghhh"), omit );
decltype(auto) o = get<2>(list);
cout << o;

元组内部元组(零开销):

TuplePiecewise< string, TuplePiecewise<int,int> > list4(forward_as_tuple("RRR"), forward_as_tuple(forward_as_tuple(10), forward_as_tuple(20)));