2014-09-21 8 views
7

imparando a conoscere iteratori in C++ ho provato la seguente:Perché il compilatore consente vector.begin() = vector.end() in C++?

#include <vector> 

int main() { 
    std::vector<int> a; 
    a.end()=a.begin(); 
    //Why is this even allowed by the compiler? 
} 

Che cosa mi manca?

+1

Perché questo deve essere espressamente proibito? E cosa ne pensi di quella linea? – Mat

+3

Bene, per quanto ne so se faccio lo stesso con una funzione membro di una classe, il compilatore si lamenta perché deve essere un lvalue modificabile. – Ant

risposta

6

La norma non specifica se std::vector::iterator è una classe digita o un puntatore non elaborato.

Se si tratta di un tipo di classe, questo codice chiama operator= sull'oggetto temporaneo restituito da a.end(). Non un'operazione molto utile, ma legale. (I valori Rvalue possono avere funzioni chiamate su di essi).

Se la tua libreria rende std::vector::iterator un puntatore allora questo codice non riuscirebbe a compilare, poiché il semplice compito richiede un lvalue a sinistra.

Jarod42 sottolineato che se operatore di assegnazione dell'iteratore era stato definito come:

std::vector::iterator& std::vector::iterator::operator =(std::vector::iterator) &; 

allora questo codice sarebbe illegale; il trailing & significa che la funzione è selezionabile solo quando viene richiamata su un lvalue.

Ma non è stato definito in questo modo, direi che il Comitato non voleva rendere illegale alcun codice legale senza una buona ragione; forse c'è un caso d'uso che nessuno ha ancora pensato.

+0

re "Direi che il Comitato non voleva rendere illegale un codice legale senza una buona ragione", molto in C++ non ha alcun motivo specifico. ma è un linguaggio pratico, quindi le cose che chiaramente non hanno funzionato sono state eliminate. poiché le regole qui permettono il codice che può essere compilato con un compilatore e non con un altro, una conclusione ragionevole è che non è mai stato un problema significativo: non è stato eliminato. –

9

non sarebbe possibile se per esempio la fine della funzione restituisse un puntatore.

Per esempio, questo codice non sarà compilato

int a[] = { 1, 2, 3 }; 

std::end(a) = std::begin(a); 

problemi GCC errore

error: lvalue required as left operand of assignment 
    std::end(a) = std::begin(a); 
       ^

Tuttavia quando gli oggetti di tipi di classe vengono utilizzati poi si può chiamare la copia ovetloaded (o spostare) operatore di assegnazione .

Quindi la domanda sorge perché le funzioni non restituiscono oggetti iteratori costanti. Penso che sarebbe possibile applicare operator ++. Per esempio

auto it = ++c.begin(); 

per iteratori ad accesso diretto come puntatori è possibile scrivere semplicemente

auto it = begin(a) + 1; 
3

La linea effetto un iteratore temporanea, e così è inutile.

std::vector::iterator = std::vector::iterator è consentito.

Un modo per non consentire che sarebbe avere

std::vector::iterator::operator =(std::vector::iterator) &; // note the extra & 

ma std::vector::iterator può essere un puntatore semplice, e non siamo in grado di disalow T* = T*

+2

Puoi spiegare esattamente cosa fa il comando '&' alla fine della funzione? – Svalorzen

+1

@Svalorzan significa che la funzione è solo un candidato valido quando viene richiamata su un lvalue –

+0

Inoltre, i qualificatori di ref sono stati introdotti nella lingua waaaay più tardi di STL. – shakurov

2

Non è possibile assegnare un tipo primitivo di rvalue (aka temporaneo) in C++, come (int)7 o un puntatore restituito da un valore di una funzione.Questo ha molto senso, poiché il risultato del compito verrebbe immediatamente scartato, quindi probabilmente non è ciò che voleva il programmatore. rvalue significa 'lato destro del segno =' da grammatiche linguistiche, o un valore temporaneo (anonimo).

Tuttavia su oggetti di classe, = invoca appena operator=. Di solito quando lhs è un valore, questo non avrebbe senso, ma a volte sarebbe (per esempio, la classe è uno pseudo-riferimento dell'oggetto proxy (come quello [] su std::vector<bool> restituisce). Quindi è trattato nit in tutti i particolari, è solo invoca il metodo. e non c'era modo di sapere se il metodo è stato richiamato da una rvalue temporaneo o un lvalue 'reale'.

riferimenti rvalue sono di nuovo in C++ 11, così come lo sono i metodi qualificati rvalue.

Prima di C++ 11, non c'era modo di bloccare il chiamare operator= su un oggetto di tipo classe solo perché era un temporaneo

come begin retu rns un temporaneo non-const, se è di tipo di classe (non garantito dallo standard) a cui può essere assegnato.

Oggi possiamo bloccare l'assegnazione ai provini del tipo di classe, ma la libreria standard non è stata modificata per bloccarla sui suoi tipi. Probabilmente almeno in parte per evitare retrocompatibilità e in parte per determinare le migliori pratiche al di fuori di std.

+1

In realtà a.begin() = a.end() è possibile anche su C++ 11 precedente. –

+0

@nipun Ho detto che non era possibile ** bloccare ** solo per rvalues ​​prima di C++ 11. O ho perso una negazione? – Yakk

+0

Penso di non aver capito correttamente quello che stai indicando. Ma potremmo sempre bloccare l'operatore rendendolo privato. Ora, con C++ 11 abbiamo cancellato funzioni membro che è un modo ancora più semplice per bloccarlo. –

1

Sono consentiti perché gli oggetti iteratori sono copiabili e copiabili. Questo è il motivo per cui possiamo fare qualcosa come a.begin() = a.end(). Inoltre, possiamo fare qualcosa di automatico (a.begin()).

Controllare gli esempi di seguito relativi alla funzione di copia.

#include <vector> 

struct CopyConsAssignable 
{ 
    int x; 
    CopyConsAssignable(int a) 
    { 
     x = a; 
    } 

    CopyConsAssignable(const CopyConsAssignable& other) 
    { 
     x = other.x; 
    } 

    CopyConsAssignable& operator=(const CopyConsAssignable& other) 
    { 
     if (this != &other) 
     { 
      x = other.x; 
     } 
     return *this; 
    } 
}; 


int main() 
{ 
    std::vector<int> a; 
    a.begin() = a.end(); 

    CopyConsAssignable(2) = CopyConsAssignable(4); // Allowed as the objects are copy-assignable 
    return 0; 
}