问题 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;
}

产量 (如预期的那样):

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

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


10875
2017-11-28 18:51


起源

我可以推断 {A{}, A{}, A{}} 在原地构造初始化列表的所有内容。 - Orient
也不可能使用 std::initializer_list 初始化不可复制对象的容器。 - Orient


答案:


最近有一个 可移动初始化列表的提议特别是作者说:

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


6
2017-11-28 19:04





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

这是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;
}

4
2017-11-28 19:09