Dies funktioniert nicht wie angegeben, da list.begin()
Typ hatconst T *
ist und Sie sich nicht von einem konstanten Objekt entfernen können. Die Sprachdesigner haben dies wahrscheinlich gemacht, damit Initialisierungslisten beispielsweise Zeichenfolgenkonstanten enthalten können, von denen es unangemessen wäre, sie zu verschieben.
Wenn Sie sich jedoch in einer Situation befinden, in der Sie wissen, dass die Initialisierungsliste rWert-Ausdrücke enthält (oder Sie den Benutzer zwingen möchten, diese zu schreiben), gibt es einen Trick, mit dem dies funktioniert (ich wurde von der Antwort von Sumant für inspiriert dies, aber die Lösung ist viel einfacher als diese). Die in der Initialisiererliste gespeicherten Elemente müssen keine T
Werte sein, sondern Werte, die kapseln T&&
. Selbst wenn diese Werte selbst const
qualifiziert sind, können sie dennoch einen veränderbaren Wert abrufen.
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
};
Anstatt ein initializer_list<T>
Argument zu deklarieren, deklarieren Sie jetzt ein initializer_list<rref_capture<T> >
Argument. Hier ist ein konkretes Beispiel mit einem Vektor von std::unique_ptr<int>
intelligenten Zeigern, für die nur die Bewegungssemantik definiert ist (daher können diese Objekte selbst niemals in einer Initialisierungsliste gespeichert werden). Die unten stehende Initialisierungsliste lässt sich jedoch problemlos kompilieren.
#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;
}
Eine Frage muss beantwortet werden: Wenn die Elemente der Initialisierungsliste echte Werte sein sollen (im Beispiel sind es x-Werte), stellt die Sprache dann sicher, dass sich die Lebensdauer der entsprechenden Provisorien bis zu dem Punkt erstreckt, an dem sie verwendet werden? Ehrlich gesagt glaube ich nicht, dass der relevante Abschnitt 8.5 des Standards dieses Problem überhaupt behandelt. Wenn man jedoch 1,9: 10 liest, scheint es, dass der relevante vollständige Ausdruck in allen Fällen die Verwendung der Initialisiererliste umfasst, so dass ich denke, dass keine Gefahr besteht, dass rvalue-Referenzen baumeln.
initializer_list<T>
sind nicht -const. Wieinitializer_list<int>
bezieht sich aufint
Objekte. Aber ich denke, das ist ein Defekt - es ist beabsichtigt, dass Compiler eine Liste statisch im Nur-Lese-Speicher zuordnen können.