为什么没有分段元组建设?(Why is there no piecewise tuple const

2019-06-26 20:09发布

该标准模板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, ...));

有没有这个不能做的理由? [ 编辑 :还是我误解分段施工的目的完全?]

(我真的有,我想初始化被默认元素值的元组,我宁愿直接从参数构建的载体的情况下,没有列明每个元组元素再次输入。)

Answer 1:

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

我的回忆是,加入分段建设, std::pair原因只有一个:支持对元素的使用,分配结构,即允许提供一个allocator,并有条件地传递给元素,如果他们支持建设与分配器(见[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 ,它负责检测是否元件应与一个分配器被构造(参见construct重载采用指针到std::pair在[allocator.adaptor.members])。

分段建设的一个很好的结果是,你可以做对元素“布设”风格的初始化,允许对不可移动,不可复制的类型,但据我所知,这不是设计的目标。

所以有理由tuple不支持它的是,该功能的发明是为了简化pair其中已经从一个非常简单的类型在C ++ 03中的C ++ 0x的笑柄激增,但这样做对于相同的tuple没有考虑同样重要(这是新的C ++ 11反正)。 此外,延长scoped_allocator_adaptor处理元素的任意数量的元组会作出该适配器的复杂得多

至于std::array ,这是一个聚合类型(因为原因),因此增加一个构造函数取piecewise_construct_t也不是没有可能使其成为一个非集合。



Answer 2:

我不知道为什么它不存在。 以前,我认为实施将是不可能的,鉴于目前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;
}


Answer 3:

下面是我实现的元组分段的(它也允许与省略值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)));


文章来源: Why is there no piecewise tuple construction?