To nie zadziała zgodnie z opisem, ponieważ list.begin()
ma typ const T *
i nie ma możliwości przeniesienia się ze stałego obiektu. Projektanci języka prawdopodobnie tak zrobili, aby listy inicjalizujące zawierały na przykład stałe łańcuchowe, z których byłoby niewłaściwe przenoszenie.
Jeśli jednak jesteś w sytuacji, w której wiesz, że lista inicjalizacyjna zawiera wyrażenia r-wartości (lub chcesz zmusić użytkownika do ich napisania), to istnieje sztuczka, która sprawi, że to zadziała (zainspirowała mnie odpowiedź Sumanta dla to, ale rozwiązanie jest znacznie prostsze niż tamto). Potrzebujesz, aby elementy przechowywane na liście inicjatorów nie były T
wartościami, ale wartościami, które są hermetyzowane T&&
. Wtedy nawet jeśli same te wartości są const
kwalifikowane, nadal mogą pobierać modyfikowalną wartość r.
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); }
};
Teraz zamiast deklarować initializer_list<T>
argument, deklarujesz initializer_list<rref_capture<T> >
argument. Oto konkretny przykład, obejmujący wektor std::unique_ptr<int>
inteligentnych wskaźników, dla którego zdefiniowano tylko semantykę przenoszenia (więc same obiekty nie mogą być nigdy przechowywane na liście inicjalizacyjnej); jednak lista inicjalizatorów poniżej kompiluje się bez problemu.
#include <memory>
#include <initializer_list>
class uptr_vec
{
typedef std::unique_ptr<int> uptr;
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;
}
Jedno pytanie wymaga odpowiedzi: jeśli elementy listy inicjalizacyjnej powinny być prawdziwymi wartościami prvalues (w przykładzie są to wartości x), czy język zapewnia, że czas życia odpowiednich tymczasowych rozciąga się do momentu, w którym są używane? Szczerze mówiąc, nie sądzę, aby odpowiednia sekcja 8.5 standardu w ogóle rozwiązała ten problem. Jednak czytając 1.9: 10, mogłoby się wydawać, że odpowiednie pełne wyrażenie we wszystkich przypadkach obejmuje użycie listy inicjalizującej, więc myślę, że nie ma niebezpieczeństwa wiszących odniesień rwartości.
initializer_list<T>
są ograniczone. Podobnie, odnosi się do przedmiotów. Ale myślę, że jest to wada - w zamierzeniu kompilatory mogą statycznie alokować listę w pamięci tylko do odczytu.initializer_list<int>
int