Questo non funzionerà come indicato, perché list.begin()
ha il tipo const T *
e non è possibile spostarsi da un oggetto costante. I progettisti del linguaggio probabilmente l'hanno fatto per consentire agli elenchi di inizializzatori di contenere, ad esempio, costanti di stringa, da cui sarebbe inappropriato spostarsi.
Tuttavia, se ti trovi in una situazione in cui sai che l'elenco degli inizializzatori contiene espressioni rvalue (o vuoi costringere l'utente a scriverle), allora c'è un trucco che lo farà funzionare (sono stato ispirato dalla risposta di Sumant per questo, ma la soluzione è molto più semplice di quella). È necessario che gli elementi memorizzati nell'elenco inizializzatore non siano T
valori, ma valori che incapsulano T&&
. Quindi, anche se questi valori stessi sono const
qualificati, possono comunque recuperare un valore modificabile.
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
};
Ora invece di dichiarare un initializer_list<T>
argomento, dichiari un initializer_list<rref_capture<T> >
argomento. Ecco un esempio concreto, che coinvolge un vettore di std::unique_ptr<int>
puntatori intelligenti, per il quale è definita solo la semantica di spostamento (quindi questi oggetti stessi non possono mai essere memorizzati in un elenco di inizializzatori); tuttavia l'elenco degli inizializzatori di seguito viene compilato senza problemi.
#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;
}
Una domanda ha bisogno di una risposta: se gli elementi della lista degli inizializzatori dovessero essere veri prvalues (nell'esempio sono xvalues), il linguaggio garantisce che la durata dei provvisori corrispondenti si estenda al punto in cui vengono utilizzati? Francamente, non credo che la sezione 8.5 pertinente dello standard affronti questo problema. Tuttavia, leggendo 1.9: 10, sembrerebbe che l' espressione completa pertinente in tutti i casi includa l'uso dell'elenco di inizializzatori, quindi penso che non ci sia pericolo di riferimenti rvalue penzolanti.
initializer_list<T>
sono non -const. Come, siinitializer_list<int>
riferisce agliint
oggetti. Ma penso che sia un difetto: è inteso che i compilatori possono allocare staticamente un elenco nella memoria di sola lettura.