Using emplace with algorithms such as std::fill(将 emplace 与 std::fill 等算法一起使用)
问题描述
我使用了 vector::emplace_back 以避免在填充向量时构造时间对象.这里有一个简化版本:
I have used vector::emplace_back in order to avoid constructing temporal objects while filling a vector. Here you have a simplified version:
class Foo {
public:
    Foo(int i, double d) : i_(i), d_(d) {}
    /* ... */
};
std::vector<Foo> v;
v.reserve(10);
for (int i = 0; i < 10; i++)
    v.emplace_back(1, 1.0);
但我想用 std::fill_n 代替:
v.reserve(10);
std::fill_n(std::back_inserter(v), 10, Foo(1, 1.0));
不过,通过这种方式,将创建临时副本.在这种情况下,我不知道如何使用 emplace.我想我需要像 std::back_emplacer 这样的东西,但我找不到这样的东西.那是 C++11 的一部分,但还没有在 GCC 中实现吗?如果它不是 C++11 的一部分,还有其他方法可以做到吗?
In this way, temporal copies will be created, though. I do not know how to use emplace in this situation. I guess I would need something like std::back_emplacer, but I could not find such a thing. Is that part of C++11, but not implemented in GCC yet? If it is not part of C++11, is there any other way to do that? 
推荐答案
通常使用元组来简化传递可变数量的项目(在这种情况下,要转发到 emplace_back 的参数),使用一个小技巧来解包元组回来.因此,通过要求用户使用元组工厂函数(std::make_tuple、std::tie, std::forward_as_tuple) 在它有意义的地方:
It's common to use tuples to ease the pass a variadic number of items (in this case, parameters to forward to emplace_back), with a little technique to unpack the tuple back. As such it is possible to write a back_emplacer utility by requiring the user to make use of the tuple factory functions (one of std::make_tuple, std::tie, std::forward_as_tuple) where it make sense:
#include <type_traits>
#include <tuple>
// Reusable utilites
template<typename T>
using RemoveReference = typename std::remove_reference<T>::type;
template<typename T>
using Bare = typename std::remove_cv<RemoveReference<T>>::type;
template<typename Out, typename In>
using WithValueCategoryOf = typename std::conditional<
    std::is_lvalue_reference<In>::value
    ,  typename std::add_lvalue_reference<Out>::type
    , typename std::conditional<
        std::is_rvalue_reference<Out>::value
        , typename std::add_rvalue_reference<Out>::type
        , Out
    >::type
>::type;
template<int N, typename Tuple>
using TupleElement = WithValueCategoryOf<
    typename std::tuple_element<N, RemoveReference<Tuple>>::type
    , Tuple
>;  
// Utilities to unpack a tuple
template<int... N>
struct indices {
    using next = indices<N..., sizeof...(N)>;
};
template<int N>
struct build_indices {
    using type = typename build_indices<N - 1>::type::next;
};
template<>
struct build_indices<0> {
    using type = indices<>;
};
template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices() { return {}; }
template<typename Container>
class back_emplace_iterator {
public:
    explicit back_emplace_iterator(Container& container)
        : container(&container)
    {}  
    template<
        typename Tuple
        // It's important that a member like operator= be constrained
        // in this case the constraint is delegated to emplace,
        // where it can more easily be expressed (by expanding the tuple)   
        , typename = decltype( emplace(std::declval<Tuple>(), make_indices<Tuple>()) )
    >
    back_emplace_iterator& operator=(Tuple&& tuple)
    {
        emplace(*container, std::forward<Tuple>(tuple), make_indices<Tuple>());
        return *this;
    }
    template<
        typename Tuple
        , int... Indices  
        , typename std::enable_if<
            std::is_constructible<
                typename Container::value_type
                , TupleElement<Indices, Tuple>...
            >::value
            , int
        >::type...
    >
    void emplace(Tuple&& tuple, indices<Indices...>)
    {
        using std::get;
        container->emplace_back(get<Indices>(std::forward<Tuple>(tuple))...);
    }
    // Mimic interface of std::back_insert_iterator
    back_emplace_iterator& operator*() { return *this; }
    back_emplace_iterator& operator++() { return *this; }
    back_emplace_iterator operator++(int) { return *this; }
private:
    Container* container;  
};
template<typename Container>
back_emplace_iterator<Container> back_emplacer(Container& c)
{ return back_emplace_iterator<Container> { c }; }
代码演示可用.在你的情况下,你想调用 std::fill_n(back_emplacer(v), 10, std::forward_as_tuple(1, 1.0)); (std::make_tuple 也可以接受).您还需要常用的迭代器来使功能完整——我为此推荐 Boost.Iterators.
A demonstration of the code is available. In your case you'd want to call std::fill_n(back_emplacer(v), 10, std::forward_as_tuple(1, 1.0)); (std::make_tuple is also acceptable). You'd also want the usual iterator stuff to make the feature complete -- I recommend Boost.Iterators for that.
但是我必须强调的是,当与 std::fill_n 一起使用时,这样的实用程序并没有带来太多好处.在您的情况下,它将保存临时 Foo 的构造,以支持引用元组(如果您要使用 std::make_tuple,则为值元组).我把它留给读者去寻找其他一些 back_emplacer 有用的算法.
I must really stress however that such a utility doesn't bring much when used with std::fill_n. In your case it would save the construction of the temporary Foo, in favour of a tuple of references (a tuple of values if you were to use std::make_tuple). I leave it to the reader to find some other algorithm where back_emplacer would be useful.
这篇关于将 emplace 与 std::fill 等算法一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:将 emplace 与 std::fill 等算法一起使用
				
        
 
            
        - 近似搜索的工作原理 2021-01-01
 - 从python回调到c++的选项 2022-11-16
 - Stroustrup 的 Simple_window.h 2022-01-01
 - 使用/clr 时出现 LNK2022 错误 2022-01-01
 - STL 中有 dereference_iterator 吗? 2022-01-01
 - 如何对自定义类的向量使用std::find()? 2022-11-07
 - 与 int by int 相比,为什么执行 float by float 矩阵乘法更快? 2021-01-01
 - 静态初始化顺序失败 2022-01-01
 - C++ 协变模板 2021-01-01
 - 一起使用 MPI 和 OpenCV 时出现分段错误 2022-01-01
 
						
						
						
						
						
				
				
				
				