40

标准模板std::pairstd::array是 的特例std::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, ...));

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

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

4

3 回答 3

17

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

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

在 C++0x 过程中的某个时刻,std::pair构造函数的数量是现在的两倍,每个构造函数都有一个相应的“分配器扩展”版本,采用一个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_adaptor,它负责检测是否应使用分配器构造元素(请参阅[allocator.adaptor.members] 中construct采用指针的重载)。std::pair

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

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

至于,这是一个聚合类型(因为原因),所以如果不使其成为非聚合,就不可能std::array添加构造函数。piecewise_construct_t

于 2014-07-22T13:02:15.847 回答
13

我不确定为什么它不存在。以前,我认为考虑到当前的 varad 模板语法,实现是不可能的,但我意识到如果它被分解成碎片是可以完成的。

如果他们定义了这样的接口:

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;
}
于 2012-08-07T14:18:34.397 回答
1

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

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)));
于 2015-08-20T16:04:18.853 回答