Это не будет работать, как указано, потому что list.begin()
имеет тип const T *
, и вы не можете перейти от постоянного объекта. Разработчики языка, вероятно, сделали это так, чтобы позволить спискам инициализаторов содержать, например, строковые константы, из которых было бы неуместно перемещаться.
Однако, если вы находитесь в ситуации, когда вы знаете, что список инициализаторов содержит выражения rvalue (или вы хотите заставить пользователя написать их), тогда есть трюк, который заставит его работать (меня вдохновил ответ Суманта для это, но решение намного проще, чем это). Вам нужно, чтобы элементы, хранящиеся в списке инициализаторов, были не T
значениями, а значениями, которые инкапсулируют T&&
. Тогда, даже если сами эти значения const
квалифицированы, они все равно могут получить изменяемое rvalue.
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); } // restitute rvalue ref
};
Теперь вместо объявления initializer_list<T>
аргумента вы объявляете initializer_list<rref_capture<T> >
аргумент. Вот конкретный пример, включающий вектор std::unique_ptr<int>
интеллектуальных указателей, для которого определена только семантика перемещения (поэтому сами эти объекты никогда не могут быть сохранены в списке инициализаторов); однако список инициализаторов ниже компилируется без проблем.
#include <memory>
#include <initializer_list>
class uptr_vec
{
typedef std::unique_ptr<int> uptr; // move only type
std::vector<uptr> data;
public:
uptr_vec(uptr_vec&& v) : data(std::move(v.data)) {}
uptr_vec(std::initializer_list<rref_capture<uptr> > l)
: data(l.begin(),l.end())
{}
uptr_vec& operator=(const uptr_vec&) = delete;
int operator[] (size_t index) const { return *data[index]; }
};
int main()
{
std::unique_ptr<int> a(new int(3)), b(new int(1)),c(new int(4));
uptr_vec v { std::move(a), std::move(b), std::move(c) };
std::cout << v[0] << "," << v[1] << "," << v[2] << std::endl;
}
На один вопрос нужен ответ: если элементы списка инициализатора должны быть истинными значениями pr (в данном примере это значения x), гарантирует ли язык, что время жизни соответствующих временных файлов продлится до точки, в которой они используются? Честно говоря, я не думаю, что соответствующий раздел 8.5 стандарта вообще решает эту проблему. Однако, читая 1.9: 10, может показаться, что соответствующее полное выражение во всех случаях включает использование списка инициализаторов, поэтому я думаю, что нет опасности висящих ссылок на rvalue.
initializer_list<T>
являются не -const. Нравится,initializer_list<int>
относится кint
объектам. Но я думаю, что это дефект - предполагается, что компиляторы могут статически размещать список в памяти только для чтения.