Это не будет работать, как указано, потому что list.begin()
имеет тип const T *
, и вы никак не можете отойти от постоянного объекта. Разработчики языка, вероятно, сделали это для того, чтобы списки инициализаторов могли содержать, например, строковые константы, из которых было бы неуместно перемещаться.
Однако, если вы находитесь в ситуации, когда вы знаете, что список инициализатора содержит выражения rvalue (или вы хотите заставить пользователя их писать), то есть хитрость, которая заставит его работать (меня вдохновил ответ Сумант за это, но решение намного проще, чем это). Вам необходимо, чтобы элементы, хранящиеся в списке инициализатора, были не значениями T
, а значениями, инкапсулирующими T&&
. Тогда даже если эти значения сами по себе являются const
квалифицированными, они все равно могут получить изменяемое значение r.
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;
}
Один вопрос нуждается в ответе: если элементы списка инициализатора должны быть истинными значениями (в данном примере они являются значениями x), обеспечивает ли язык время жизни соответствующих временных значений до точки, где они используются? Честно говоря, я не думаю, что соответствующий раздел 8.5 стандарта решает эту проблему вообще. Однако, читая 1.9: 10, может показаться, что соответствующее полное выражение во всех случаях охватывает использование списка инициализаторов, поэтому я думаю, что нет опасности оборванных ссылок на rvalue.