2016-05-16 17 views
2

Aufruf der Memberfunktion .begin() von std::vector und std::begin() auf rvalues ​​führen zu unterschiedlichen Ausgängen, wie der folgende Test zeigt:Mitglied Funktion .begin() und std :: begin()

vector<int> a{ 1, 2, 3 }; 

vector<int>::iterator it1 = move(a).begin(); // OK 
vector<int>::const_iterator it2 = move(a).begin(); // OK 

vector<int>::iterator it3 = begin(move(a)); // Error! 
vector<int>::const_iterator it4 = begin(move(a)); // OK 

Hier mein Verständnis ist: std::begin() Aufrufe const& Überlast (da es && Überlast fehlt), und daher ein const_iterator Objekt zurückgeben. So kann der Rückgabewert zu const_iterator zugewiesen werden, aber nicht iterator.

  1. Ist mein Verständnis richtig?
  2. Warum hat std::begin() keine Rvalue-Überlastung?

Nur eine Anmerkung, die ich move(a) verwendet zu demonstrieren .begin() und std::begin() auf rvalues ​​aufrufen. Natürlich kann es durch jedes rvalue-Objekt ersetzt werden, für das .begin() und std::begin() gut definiert sind.

Bearbeiten: Hier ist das reale Beispiel zeigt, wo ich dieses Problem festgestellt habe. Ich habe viel vereinfacht, nur um die Idee zu vermitteln, wo std::begin() auf einem rvalue aufgerufen wird. Also, da row_matrix eine Proxy-Klasse ist, sollte es kein Problem geben, begin und end auf Rvalues ​​aufrufen, da das zugrunde liegende Objekt identisch ist.

class matrix_row; 
class row_iterator; 

class matrix { 
public: 
    matrix_row row(int i); 
    // other members 
}; 

class matrix_row { // <- proxy class representing a row of matrix 
public: 
    row_iterator begin(); 
    row_iterator end(); 
    // other members 
private: 
    int row_; 
    matrix& matrix_; 
}; 

class row_iterator { 
    // defined everything needed to qualify as a valid iterator 
}; 

matrix m(3,4); 

for(auto x = m.row(1).begin(); x != m.row(1).end(); ++x) { 
    *x /=2; // OK 
} 

for(auto x = begin(m.row(1)); x != end(m.row(1)); ++x) { 
    *x /= 2; // Error 
} 
+4

[Dass 'std :: begin()' akzeptiert einen Rvalue überhaupt ist wahrscheinlich eher ein Unfall als beabsichtigte Design.] (Http://StackOverflow.com/questions/33586029/what-is-the-purpose-) of-the-const-Überladungen-von-stdbegin-and-end) –

+2

* "Warum hat' std :: begin() 'keine Rvalue-Überladung?" * Was würde das tun? Klingt mir nicht allzu nützlich. –

+2

'begin' ist nicht besonders nützlich, wenn der zugrundeliegende Bereich nicht mehr existiert. Daher erscheint es nicht sinnvoll, eine Rvalue-Überladung zu haben. –

Antwort

-1

23.2.1/12 Zustände:

Wenn nicht anders angegeben (entweder explizit oder durch eine Funktion in Bezug auf die anderen Funktionen zu definieren), ein Behälterelement Funktion oder Hindurchleiten eines Behälters als Aufruf Ein Argument für eine Bibliotheksfunktion darf Iteratoren nicht ungültig machen oder die Werte von Objekten innerhalb dieses Containers ändern.

DR 2321 will auch sie explizit machen:

keine Bewegung Konstruktor (oder Zuweisungsoperator bewegen, wenn allocator_traits<allocator_type>::propagate_on_container_move_assignment::value wahr ist) einen Behälter (außer array) entkräftet alle Referenzen, Zeiger, oder Iteratoren, die sich auf die Elemente des Quellcontainers beziehen. [Hinweis: Der end() Iterator bezieht sich nicht auf ein Element, daher kann es ungültig werden. - Endnote]

Dies ist natürlich nur sinnvoll, wenn Sie wurden in einen anderen Behälter zu bewegen, aber du bist nicht.

+1

Was hat das mit der Frage zu tun? –

2

Bis vor kurzem war das Überladen .begin() durch rvalue/lvalue-ness des aufrufenden Objekts nicht möglich.

Wenn es hinzugefügt wurde, könnte die Nachrüstung solcher Änderungen in der Standardbibliothek theoretisch bestehenden Code brechen.

Der vorhandene Code zu brechen ist schlecht, schlimm genug, dass Legacy-Macken in ausreichendem Maße den Beweis dafür blockieren, dass ein solcher Code nicht existiert, dass es klare Diagnosen geben würde und/oder die Auswirkung der Änderung wirklich nützlich ist.

So .begin() ignoriert Rvalueß seiner *this.

Es gibt keine solche Beschränkung auf std::begin, andere als möglicherweise wünschen Kompatibilität mit .begin().

Theoretisch haben Standardcontainer keine richtige Antwort mit std::begin in einem Rvalue-Kontext aufgerufen werden. Die "richtige" Art der Interaktion mit std::move oder rvalues ​​besteht darin, dass Sie sich nicht um den Status des verschobenen Objekts kümmern sollen, nachdem der Aufruf abgeschlossen wurde.

Dies bedeutet (logisch), dass Sie nur einen der beiden Iteratoren (Anfang oder Ende) abrufen können.

Was die richtige Semantik in diesem Fall ist, ist ein großes Rätsel. Ich habe Adapter geschrieben, die in dieser Situation (ein Pseudo-Anfangs-/Endaufruf auf einem R-Wert) Bewegungsiteratoren erzeugen (zum Beispiel), aber das im Allgemeinen zu tun, ist sehr überraschend, und ich denke, dass es am Ende ein schlechter Zug ist.

Verwandte Themen