【问题标题】:Why is there no piecewise tuple construction?为什么没有分段元组构造?
【发布时间】:2012-08-04 12:10:41
【问题描述】:

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

这有什么不能做到的原因吗? [编辑:还是我完全误解了分段构造的目的?]

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

【问题讨论】:

  • 它在 N3059 中,在 N3140 中又消失了。现在,如果人类能够找出实际集成的报告,我就会知道它发生了什么......
  • 所以我很清楚,你有一个带有多参数构造函数的类型元组向量?
  • @MarkB:不完全是。我有一个元组类型,我想直接从组成类型的构造函数的参数构造这样一个元组,而不需要中间复制初始化。这是从 C++03 到 C++11 的一般习惯用法转换的一部分,它尽可能选择直接初始化而不是复制初始化。
  • 在什么意义上 std::array 是 std::tuple 的一个特例? std::tuple 是否有连续的内存限制?
  • @rhalbersma std::array 支持“类似元组的访问”接口,但相似性到此为止。那里也没有分段构造,也没有,因为它是一个聚合,不能有显式的构造函数。

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


【解决方案1】:

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

我的回忆是,将分段构造添加到 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,它负责检测是否应使用分配器构造元素(请参阅construct 重载中的指向std::pair 的指针[allocator.adaptor.members])。

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

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

至于std::array,这是一个聚合类型(因为某些原因),因此如果不将其设为非聚合,就不可能添加采用piecewise_construct_t 的构造函数。

【讨论】:

  • 我非常需要元组的分段构造!它很有用。
【解决方案2】:

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

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

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

并要求参数是您可以使用std::get&lt;N&gt; 访问参数(基本上是元组、对、数组)的东西。必须进行额外的检查来验证给定的参数数量与元组中的元素数量是否不匹配。

编辑:自从我阅读它以来,这个问题一直困扰着我。我创建了以下类,它派生自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;
}

【讨论】:

  • +1 虽然我不太同意array 部分,但要点(为什么没有元组的分段构造函数)是好的。在std::array 的情况下,可以在不向std::array 添加构造函数的情况下提供make_array,并且实现实际上很简单。虽然我认为它不会提供太多价值。
  • 我不认为这是一个问题:template&lt;class... Tuples&gt; tuple(piecewise_construct_t, Tuples&amp;&amp;...)。您可以添加一个特征来检查所有 Tuples 是否真的属于 std::tuple 类型。
  • @Xeo: Hrm...你是对的,你可以使用 static_assert 来验证参数的长度。而且您甚至不必只使用std::tuple(尽管这是最有可能使用的)。
  • 另一种解决方案是允许实现定义的参数数量限制与通常的包长度限制分开。解决双变量问题的方法与 C++03 中解决单变量问题的方法相同。
  • 据记录,std::piecewise_construct 的卖点之一是它允许就地构造不可移动类型(当这种类型需要多个参数时)。然而,最大的问题是只有一种方法可以通过委托构造函数来实现分段构造——这使得它非常具有侵入性。分段构造函数必须在 std::tuple 中才能获得真正的好处。
【解决方案3】:

这是我对 tuple 分段的实现(它还允许使用 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)));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-12
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2010-10-24
    • 2011-05-17
    相关资源
    最近更新 更多