Необязательный характер initializer_list приводит к чрезмерному копированию
Почему доступ к std::initializer_list
не позволяет нам изменять его содержимое? Это большой недостаток std::initializer_list
при его использовании для его основной цели (для инициализации контейнера), поскольку он использует избыточное копирование/копирование, вместо move-construction/move-assign.
#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 &)
Почему его конструкция настолько ограничена?
Ответы
Ответ 1
Существует недавнее предложение для перемещаемых списков инициализаций, где, в частности, авторы говорят:
std::initializer_list
был разработан около 2005 года (N1890) до 2007 года (N2215), до сдвинуть семантику, созревшую в 2009 году. В то время не предполагалось, что семантика копирования будет недостаточным или даже субоптимальным для общих классов, подобных значению. Был 2008 год Предложение N2801 Инициализатор перечисляет и перемещает семантику, но С++ 0x уже чувствовал, что он ускользает в то время, и к 2011 году дело стало холодным.
Ответ 2
хороший (если несчастливый) ответ Антона.
Здесь исходный код реализации в libС++:
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;
}