initializer_list不可变性导致过度复制

时间:2014-11-28 18:51:54

标签: c++ c++11 move-semantics c++14 initializer-list

为什么访问std::initializer_list不允许我们更改其内容?当std::initializer_list用于其主要目的(初始化容器)时,它是一个很大的缺点,因为它的使用会导致过多的复制构造/复制分配,而不是移动构造/动赋值。

#include <initializer_list>
#include <iostream>
#include <vector>

#include <cstdlib>

struct A
{

    A() = default;
    A(A const &) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    A(A &&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    A & operator = (A const &) { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; }
    A & operator = (A &&) { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; }

};

int
main()
{
    std::vector< A >{A{}, A{}, A{}};
    return EXIT_SUCCESS;
}

Output(正如所料):

A::A(const A &)
A::A(const A &)
A::A(const A &)

为什么它的设计如此受限?

2 个答案:

答案 0 :(得分:6)

最近有proposal for movable initializer lists,特别是作者说:

  

std::initializer_list是在2005年(N1890)到2007年(N2215)之前设计的   移动语义在2009年左右成熟。当时,没有预料到复制语义   对于类似普通价值的类来说,它是不够的,甚至是次优的。有一个2008年   提议N2801 初始化程序列出并移动语义但是当时C ++ 0x已经感觉到正在下滑,到2011年情况已经变冷了。

答案 1 :(得分:4)

好的(如果不幸的话)安东回答。

这是libc ++中实现的源代码:

template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
vector<_Tp, _Allocator>::vector(initializer_list<value_type> __il)
{
#if _LIBCPP_DEBUG_LEVEL >= 2
    __get_db()->__insert_c(this);
#endif
    if (__il.size() > 0)
    {
        allocate(__il.size());
        __construct_at_end(__il.begin(), __il.end());
    }
}

没有移动迭代器,因此复制构造。

如果它有用,这里是一个使用可变参数列表的解决方法:

#include <initializer_list>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <utility>

#include <cstdlib>

struct A
{

    A() noexcept{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
    A(A const &) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    A & operator  = (A const &) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; }
    A(A &&) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    A & operator = (A &&) noexcept { std::cout << __PRETTY_FUNCTION__ << std::endl; return *this; }

};

template<class T, class...Args>
void append_it(std::vector<T>& v)
{
}

template<class T, class...Args>
void append_it(std::vector<T>& v, T&& t1, Args&&...args)
{
    v.push_back(std::move(t1));
    append_it(v, std::forward<Args&&>(args)...);
}

template<class T, class...Args>
std::vector<T> make_vector(T&& t1, Args&&...args)
{
    std::vector<T> result;
    result.reserve(1 + sizeof...(args));
    result.push_back(std::move(t1));
    append_it(result, std::forward<Args&&>(args)...);
    return result;
}

int
main()
{
    auto v2 = make_vector( A{}, A{}, A{} );

    return EXIT_SUCCESS;
}