C++ 11 ha rimosso il requisito che il tipo di valore di tutti i contenitori sia CopyConstructible e Assignable (sebbene le operazioni specifiche sui contenitori possano imporre tali requisiti). In teoria, ciò dovrebbe consentire di definire, ad esempio, std::deque<const Foo>
, cosa che non era possibile in C++ 03.Cosa significa "Assegnabile"?
Inaspettatamente, gcc 4.7.2 ha prodotto il suo solito vomito di errori incomprensibili [1] quando ho provato questo, ma clang almeno ha reso l'errore leggibile e clang con libC++ lo ha compilato senza errori.
Ora, quando due diversi compilatori producono risultati diversi, mi viene sempre da chiedersi qual è la risposta corretta, quindi ho cercato tutti i riferimenti che ho trovato per const/assegnabili/tipi/contenitori di valori, ecc., Ecc. Ho trovato quasi un decennio di domande e risposte molto simili, alcune di queste qui su SO e altre nelle varie mailing list C++, tra le altre cose, incluso il bugan Gnu, che fondamentalmente possono essere riassunte come segue.
D: Perché non posso dichiarare std::vector<const int>
(come un esempio semplificato)
A: Perché sulla terra si vuole farlo? È senza senso.
Q: Beh, per me ha senso. Perché non posso farlo?
A: Perché lo standard richiede che i tipi di valore siano assegnabili.
Q: Ma non ho intenzione di assegnarli. Voglio che siano costanti dopo che li ho creati.
A: Questo non è il modo in cui funziona. Prossima domanda!
con un ardito lieve:
A2: C++ 11 ha deciso di permetterlo. Dovrai solo aspettare. Nel frattempo, ripensa il tuo ridicolo design.
Questi non sembrano risposte molto convincenti, anche se forse sono di parte perché rientrano nella categoria di "ma ha senso per me". Nel mio caso, mi piacerebbe avere un contenitore tipo stack in cui le cose spinte in pila siano immutabili finché non vengono spuntate, il che non mi sembra una cosa particolarmente strana da voler essere in grado di esprimere con un tipo sistema.
In ogni caso, ho iniziato a pensare la risposta, "la norma richiede tipi di valore tutti i contenitori per essere assegnabili." E, per quanto posso vedere, ora che ho trovato una vecchia copia di una bozza dello standard C++ 03, è vero; lo ha fatto.
D'altra parte, il tipo di valore di std::map
è std::pair<const Key, T>
che non mi sembra sia assegnabile. Tuttavia, ho provato di nuovo con std::deque<std::tuple<const Foo>>
e gcc ha proceduto alla compilazione senza battere ciglio. Quindi almeno ho una sorta di soluzione alternativa.
Poi ho provato a stampare il valore di std::is_assignable<const Foo, const Foo>
e std::is_assignable<std::tuple<const Foo>, const std::tuple<const Foo>>
, e si scopre che il primo è segnalato come non assegnabile, come ci si aspetterebbe, ma quest'ultimo è segnalato come assegnabile (da clang e gcc). Certo, non è veramente assegnabile; il tentativo di compilare a = b;
viene respinto da gcc con il reclamo error: assignment of read-only location
(questo era solo l'unico messaggio di errore che ho incontrato in questa ricerca che era in realtà facile da capire). Tuttavia, senza il tentativo di fare un incarico, sia clang che gcc sono ugualmente felici di istanziare lo deque<const>
e il codice sembra funzionare bene.
Ora, se è davvero std::tuple<const int>
assegnabile, quindi non posso lamentare l'incoerenza nello standard C++03
- e, in realtà, chi se ne frega - ma trovo inquietante che due diverse implementazioni della libreria standard riferiscono che un tipo è assegnabile quando, infatti, il tentativo di assegnargli un riferimento porterà ad un (molto sensibile) errore del compilatore. Potrei a un certo punto voler utilizzare il test in un modello SFINAE e, in base a ciò che ho visto oggi, non sembra molto affidabile.
Quindi c'è qualcuno che può far luce sulla domanda (nel titolo): Cosa significa Assignable veramente? E due bonus domande:
1) ha fatto il comitato significa veramente per consentire istanziare contenitori con const
tipi di valore, o hanno avere qualche altro caso non cedibile in mente ?, e
2) Esiste davvero una differenza significativa tra le costanze di const Foo
e std::tuple<const Foo>
?
[1] Per i veri curiosi, ecco il messaggio di errore prodotto da gcc quando si cerca di compilare la dichiarazione di std::deque<const std::string>
(con qualche linea di terminazioni aggiunto, e una spiegazione se si scorre eccessivamente verso il basso):
In file included from /usr/include/x86_64-linux-gnu/c++/4.7/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/string:43,
from /usr/include/c++/4.7/random:41,
from /usr/include/c++/4.7/bits/stl_algo.h:67,
from /usr/include/c++/4.7/algorithm:63,
from const_stack.cc:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<const std::basic_string<char> >’:
/usr/include/c++/4.7/bits/allocator.h:89:11: required from ‘class std::allocator<const std::basic_string<char> >’
/usr/include/c++/4.7/bits/stl_deque.h:489:61: required from ‘class std::_Deque_base<const std::basic_string<char>, std::allocator<const std::basic_string<char> > >’
/usr/include/c++/4.7/bits/stl_deque.h:728:11: required from ‘class std::deque<const std::basic_string<char> >’
const_stack.cc:112:27: required from here
/usr/include/c++/4.7/ext/new_allocator.h:83:7:
error: ‘const _Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> >::address(
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference) const [
with _Tp = const std::basic_string<char>;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_pointer =
const std::basic_string<char>*;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference =
const std::basic_string<char>&]’ cannot be overloaded
/usr/include/c++/4.7/ext/new_allocator.h:79:7:
error: with ‘_Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> >::address(
__gnu_cxx::new_allocator< <template-parameter-1-1> >::reference) const [
with _Tp = const std::basic_string<char>;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::pointer = const std::basic_string<char>*;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::reference = const std::basic_string<char>&]’
Allora, cosa sta succedendo qui è che lo standard (§ 20.6.9.1) insiste sul fatto che l'allocatore di default hanno funzioni membro:
pointer address(reference x) const noexcept;
const_pointer address(const_reference x) const noexcept;
ma se lo si istanzia con un argomento modello const
(che è apparentemente UB), quindi reference
e const_reference
sono dello stesso tipo e quindi le dichiarazioni vengono duplicate. (Il corpo della definizione è identico, per quello che vale.) Di conseguenza, nessun contenitore abilitato per l'allocatore può gestire un tipo di valore esplicitamente const
. Nascondere lo const
all'interno di uno tuple
consente all'allocatore di creare un'istanza. Questo requisito degli allocatori dello standard è stato utilizzato per giustificare la chiusura di almeno un paio di vecchi bug libstdC++ relativi ai problemi con std::vector<const int>
, sebbene non mi colpisca come un solido punto di principio. Anche libC++ risolve il problema nel modo ovvio semplice, ovvero fornire una specializzazione di allocator<const T>
con le dichiarazioni di funzioni duplicate rimosse.
IIRC il requisito Assignable non viene rimosso, è diviso in due (per copie e per mosse) –
Non hai già capito che il problema non sono i requisiti imposti da 'std :: deque' (o sequenza contenitori in generale), ma per 'std :: allocator'? Piuttosto che _questo significato assegnabile_, la tua domanda non dovrebbe essere corretta se si utilizza un allocatore che funziona con 'const' types_ (tenendo presente che' std :: allocator' non funziona con i tipi 'const')? – jogojapan
@ BenVoigt: è davvero finito. Era il 23.1 (3): "Il tipo di oggetti memorizzati in questi componenti deve soddisfare i requisiti dei tipi CopyConstructible (20.1.3) e i requisiti aggiuntivi dei tipi Assegnabili". Quel paragrafo, il prossimo, e la tabella associata 64, (riferendosi ad Assignable) sono tutti spariti. Il requisito rimanente è che se si utilizza un costruttore di copia o un'assegnazione di copia sul contenitore, il tipo di valore T deve essere CopyInsertable nel tipo di contenitore X. – rici