Quali modifiche rivoluzionarie sono state introdotte in C ++ 11?


227

So che almeno una delle modifiche in C ++ 11 che causerà l'interruzione della compilazione di alcuni vecchi codici: l'introduzione di explicit operator bool() nella libreria standard, in sostituzione di vecchie istanze di operator void*(). Certo, il codice che questo si romperà è probabilmente un codice che non avrebbe dovuto essere valido in primo luogo, ma è comunque un cambiamento decisivo: i programmi che erano validi non lo sono più.

Ci sono altri cambiamenti di rottura?


1
Rimozione del significato della exportparola chiave? Mi prenderò il cappotto.
Steve Jessop,

7
Sai, non lo definirei il cambiamento di conversione in bool un "cambiamento di rottura" ... più come un "cambiamento di punizione".
Xeo,

4
Quando tutte le scartoffie necessarie per creare una tale unione stanno solo aspettando di essere timbrate, perché no?
Dennis Zickefoose,

3
@Xeo: mystream.good()non è lo stesso di bool(mystream)? good()è vero se non è impostato alcun flag. bool(mystream)è ancora falso se eofbitè impostato solo . !mystream.fail()sarebbe l'equivalente corretto.
R. Martinho Fernandes,

2
Nota del moderatore : " Si prega di tenere commenti sull'argomento con la domanda o la risposta a portata di mano. Quando si discute di una domanda o risposta, la discussione dovrebbe riguardare proprio questo, la domanda o la risposta a portata di mano. Il dibattito, in generale, non è costruttivo per Stack Overflow. Antagonizzare sicuramente no. "
Tim Post

Risposte:


178

Il FDIS ha una sezione per le incompatibilità, nell'appendice C.2"C ++ e ISO C ++ 2003".

Riepilogo, parafrasando qui il FDIS, per renderlo (migliore) adatto come risposta SO. Ho aggiunto alcuni miei esempi per illustrare le differenze.

Ci sono alcune incompatibilità legate alle biblioteche in cui non conosco esattamente le implicazioni di, quindi lascio a quelle che gli altri possono approfondire.

Linguaggio di base


#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"

#define _x "there"
"hello"_x // now a user-defined-string-literal. Previously, expanded _x .

Nuove parole chiave: alignas, alignof, char16_t, char32_t, constexpr, decltype, noexcept, nullptr, static_assert e thread_local


Alcuni letterali interi più grandi di quelli che possono essere rappresentati da long potrebbero passare da un tipo intero senza segno a long long.


Il codice C ++ 2003 valido che utilizza la divisione intera arrotonda il risultato verso 0 o verso l'infinito negativo, mentre C ++ 0x arrotonda sempre il risultato verso 0.

(certamente non è un problema di compatibilità per la maggior parte delle persone).


Il codice C ++ 2003 valido che utilizza la parola chiave autocome identificatore della classe di archiviazione potrebbe non essere valido in C ++ 0x.


Le conversioni restrittive causano incompatibilità con C ++ 03. Ad esempio, il seguente codice è valido in C ++ 2003 ma non valido in questo standard internazionale perché double in int è una conversione restrittiva:

int x[] = { 2.0 };

Le funzioni membro speciali dichiarate implicitamente sono definite come cancellate quando la definizione implicita sarebbe stata male formata.

Un programma C ++ 2003 valido che utilizza una di queste funzioni membro speciali in un contesto in cui la definizione non è richiesta (ad es. In un'espressione che non è potenzialmente valutata) diventa mal formato.

Esempio da parte mia:

struct A { private: A(); };
struct B : A { };
int main() { sizeof B(); /* valid in C++03, invalid in C++0x */ }

Alcuni trucchi sono stati usati da alcuni SFINAE e ora devono essere cambiati :)


I distruttori dichiarati dall'utente hanno una specifica di eccezione implicita.

Esempio da parte mia:

struct A {
  ~A() { throw "foo"; }
};

int main() { try { A a; } catch(...) { } }

Questo codice chiama terminatein C ++ 0x, ma non in C ++ 03. Perché la specifica dell'eccezione implicita di A::~Ain C ++ 0x è noexcept(true).


Una dichiarazione C ++ 2003 valida contenente non exportè formata in C ++ 0x.


Un'espressione C ++ 2003 valida contenente >immediatamente seguita da un'altra >può ora essere trattata come chiusura di due modelli.

In C ++ 03, >>sarebbe sempre il token dell'operatore shift.


Consenti chiamate dipendenti di funzioni con collegamento interno.

Esempio da parte mia:

static void f(int) { }
void f(long) { }

template<typename T>
void g(T t) { f(t); }

int main() { g(0); }

In C ++ 03, questo chiama f(long), ma in C ++ 0x, questo chiama f(int). Va notato che in C ++ 03 e C ++ 0x, le seguenti chiamate f(B)(il contesto di istanza considera ancora solo dichiarazioni di collegamento esterne).

struct B { };
struct A : B { };

template<typename T>
void g(T t) { f(t); }

static void f(A) { }
void f(B) { }

int main() { A a; g(a); }

La corrispondenza migliore f(A)non viene presa, perché non ha un collegamento esterno.


Modifiche alla libreria

Il codice C ++ 2003 valido che utilizza qualsiasi identificatore aggiunto alla libreria standard C ++ di C ++ 0x potrebbe non riuscire a compilare o produrre risultati diversi in questo standard internazionale.


Il codice C ++ 2003 valido che #includesindica le intestazioni con i nomi delle nuove intestazioni della libreria standard C ++ 0x potrebbe non essere valido in questo standard internazionale.


<algorithm>Potrebbe essere necessario includere il codice C ++ 2003 valido che è stato compilato in attesa di swap<utility>


Lo spazio dei nomi globale posixè ora riservato alla standardizzazione.


Valido codice C ++ 2003 che definisce override, final, carries_dependency, o noreturncome macro non sono validi in C ++ 0x.


"Consenti chiamate dipendenti di funzioni con collegamento interno." Potresti per favore approfondire la differenza tra i tuoi due esempi? Mi manca chiaramente qualcosa.
Dennis Zickefoose,

@Dennis la modifica è stata introdotta da open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#561 . Sebbene non commentino il fatto, il "contesto di istanza" consiste ancora solo di "l'insieme di dichiarazioni con collegamento esterno dichiarate prima del punto di istanza della specializzazione del modello nella stessa unità di traduzione". Pertanto, la modifica apportata ha effetto solo sulla ricerca nel contesto della definizione.
Johannes Schaub - litb

Nel mio primo esempio, la funzione di collegamento interno era visibile e trovata nel contesto di definizione del modello. Nel mio secondo esempio, la funzione di collegamento interno dovrebbe essere parte del contesto di istanza da trovare. Ma poiché non lo è, non può essere trovato.
Johannes Schaub - litb

Per inciso, penso che l'unico caso in cui per un contesto di definizione di modello sia sicuro trovare una funzione con collegamento interno sia quando la specializzazione del modello di funzione viene istanziata in modo esplicito solo in una TU (dove viene definito il modello), e tutte le altre TU si basano su quell'istanza esplicita. In tutti gli altri casi (in cui le altre TU avrebbero istanziato la specializzazione da soli), si violerebbe l'ODR facendo sì che la definizione del modello utilizzi ogni volta una funzione (collegamento interno) diversa.
Johannes Schaub - litb

Quindi non sono sicuro del motivo per cui hanno mantenuto la restrizione sul contesto di istanza: ci sarebbe solo un'istanza (esplicita) e quell'istanza userebbe le funzioni di collegamento interno trovate nel contesto di istanza della TU di istanza. Proprio come sarebbe per il contesto di definizione. Per inciso, penso che se avessimo ancora export, quindi penso che le altre TU non avrebbero bisogno di fare affidamento sull'istanza esplicita, ma potrebbero istanziare il modello da soli. Quindi farebbe la differenza se le funzioni di collegamento interno sono o meno visibili nel contesto di istanza.
Johannes Schaub - litb

28

Il significato della parola chiave automatica è cambiato.


9
Se hai utilizzato la autoparola chiave, qualcosa non va nel tuo codice. Perché mai lo useresti?
Elazar Leibovich,

Non è un cambiamento decisivo . Ogni utilizzo valido di C ++ 03 autorimane valido in C ++ 11.
Drew Dormann,

11
@DrewDormann int main() { auto int i = 0; return i; }è C ++ 03 perfettamente valido, ma un errore di sintassi in C ++ 11. L'unico avvertimento che posso ottenere dai compilatori per farlo in modalità C ++ 03 è un avvertimento sulla compatibilità.

24

Rompere il cambiamento?

Beh, per prima cosa, se è stato utilizzato decltype, constexpr, nullptr, ecc come identificatori allora si può essere in difficoltà ...


21

Alcune incompatibilità principali che non sono coperte dalla sezione incompatibilità:


C ++ 0x considera il nome della classe iniettata come modello, se il nome viene passato come argomento a un parametro del modello di modello e come tipo se viene passato a un parametro del tipo di modello.

Il codice C ++ 03 valido può comportarsi in modo diverso se si basa sul nome della classe iniettata per essere sempre un tipo in questi scenari. Codice di esempio preso dal mio clang PR

template<template<typename> class X>
struct M { };

template<template<typename> class X>
void g(int = 0); // #1

template<typename T>
void g(long = 0); // #2

template<typename T>
struct A {
  void f() {
    g<A>(); /* is ambiguous in C++0x */
    g<A>(1); /* should choose #1 in C++0x */
  }
};

void h() {
  A<int> a;
  a.f();
}

In C ++ 03, il codice chiama la seconda gentrambe le volte.


C ++ 0x rende alcuni nomi che erano dipendenti in C ++ 03 ora non dipendenti. Richiede la ricerca del nome per i nomi qualificati non dipendenti che fanno riferimento ai membri del modello di classe corrente da ripetere all'istanza e richiede la verifica che questi nomi vengano cercati allo stesso modo del contesto di definizione del modello.

Il codice C ++ 03 valido che dipende dalla regola di dominanza ora potrebbe non essere più compilato a causa di questa modifica.

Esempio:

struct B { void f(); };

template<typename T>
struct A : virtual B { void f(); };

template<typename T>
struct C : virtual B, A<T> {
  void g() { this->f(); }
};

int main() { C<int> c; c.g(); }

Questo codice C ++ 03 valido che chiama A<int>::fnon è valido in C ++ 0x, perché la ricerca del nome durante l'istanza trova A<int>::finvece di quella B::f, causando un conflitto con la ricerca alla definizione.

A questo punto, non è chiaro se si tratti di un difetto nell'FDIS. Il comitato ne è consapevole e valuterà la situazione.


Una dichiarazione di utilizzo in cui l'ultima parte è uguale all'identificatore nell'ultima parte del qualificatore nel nome qualificato che indica una classe di base, che usando la dichiarazione ora nomina il costruttore, anziché i membri con quel nome.

Esempio:

struct A { protected: int B; };
typedef A B;

struct C : B {
  // inheriting constructor, instead of bringing A::B into scope
  using B::B;
};

int main() { C c; c.B = 0; }

Il codice di esempio sopra è ben formato in C ++ 03, ma mal formato in C ++ 0x, come A::Bè ancora inaccessibile in main.


14

L'errore di estrazione del flusso viene trattato in modo diverso.

Esempio

#include <sstream>
#include <cassert>

int main()
{
   std::stringstream ss;
   ss << '!';
   
   int x = -1;
   
   assert(!(ss >> x)); // C++03 and C++11
   assert(x == -1);    // C++03
   assert(x == 0);     // C++11
}

Modifica proposta

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3246.html#23

Riferimento standard

[C++03: 22.2.2.1.2/11]: Il risultato dell'elaborazione della fase 2 può essere uno dei

  • Una sequenza di caratteri è stata accumulata nella fase 2 che viene convertita (secondo le regole di scanf) in un valore del tipo di val. Questo valore è memorizzato in vale ios_base::goodbitmemorizzato in err.
  • La sequenza di caratteri accumulati nella fase 2 avrebbe causato scanfun errore di input. ios_base::failbitè assegnato a err. [ed: Nulla è memorizzato in val.]

[C++11: 22.4.2.1.2/3]: [..] Il valore numerico da memorizzare può essere uno dei seguenti:

  • zero, se la funzione di conversione non riesce a convertire l'intero campo . ios_base::failbitè assegnato a err.
  • il valore rappresentabile più positivo, se il campo rappresenta un valore troppo grande positivo per essere rappresentato val. ios_base::failbitè assegnato a err.
  • il valore rappresentabile più negativo o zero per un tipo intero senza segno, se il campo rappresenta un valore negativo troppo grande per essere rappresentato val. ios_base::failbitè assegnato a err.
  • il valore convertito, altrimenti.

Il valore numerico risultante è memorizzato in val.

implementazioni

  • GCC 4.8 emette correttamente per C ++ 11 :

    Asserzione `x == -1 'non riuscita

  • GCC 4.5-4.8 tutto l'output per C ++ 03 il seguente, che sembrerebbe essere un bug:

    Asserzione `x == -1 'non riuscita

  • Visual C ++ 2008 Express restituisce correttamente per C ++ 03:

    Asserzione non riuscita: x == 0

  • Visual C ++ 2012 Express genera in modo errato per C ++ 11, che sembrerebbe essere un problema relativo allo stato di implementazione:

    Asserzione non riuscita: x == 0


13

In che modo l'introduzione degli operatori di conversione esplicita è un cambiamento radicale? La vecchia versione sarà ancora "valida" come prima.

Sì, il cambiamento da operator void*() consta explicit operator bool() constsarà un cambiamento di rottura, ma solo se viene utilizzato in modo errato dentro e fuori di sé. Il codice conforme non verrà infranto.

Ora, un altro cambiamento sostanziale è il divieto di restringere le conversioni durante l'inizializzazione aggregata :

int a[] = { 1.0 }; // error

Modifica : solo il promemoria std::identity<T>verrà rimosso in C ++ 0x (vedere la nota). È una struttura di convenienza per rendere dipendenti i tipi. Dal momento che la struttura non fa davvero molto, questo dovrebbe risolverlo:

template<class T>
struct identity{
  typedef T type;
};

Se agli oggetti della libreria standard sono state aggiunte conversioni esplicite, le conversioni implicite esistenti potrebbero smettere di funzionare. Ma non riesco a immaginare uno scenario in cui la conversione non sarebbe valida e fare qualcosa di utile.
Dennis Zickefoose

L'introduzione è un cambiamento radicale perché sostituirà quello esistente operator void*.
R. Martinho Fernandes

@Dennis: Aaah, ora capisco cosa voleva dire @Martinho. Ma sarà un cambiamento decisivo solo se le persone lo usassero diversamente da quanto previsto.
Xeo

"ma solo se viene utilizzato in un modo che è sbagliato dentro e fuori da sé" - bool ok = cin >> a; cout << "done reading" << endl; if (ok) { ... }Non c'è nulla di veramente sbagliato in C ++ 03, eppure è diventato un errore in C ++ 11. (Nota: GCC 4.9 ha ancora operator void*() constqui, motivo per cui accetta il codice in modalità C ++ 11.)

std::identity<T>non è stato rimosso in C ++ 11, perché non faceva parte di C ++ 03. Esisteva brevemente nella bozza per C ++ 11 e fu rimosso dalla bozza prima della standardizzazione.
Howard Hinnant,


7

Si è discusso molto della mossa implicita che rompe la compatibilità con le versioni precedenti

( una pagina precedente con discussione pertinente )

Se si legge nei commenti, anche il ritorno di spostamento implicito è un cambiamento decisivo.


Il risultato di queste discussioni è che è stato rimosso in quasi tutti i casi. Ci sono problemi con ciò che è rimasto?
Dennis Zickefoose,

@Dennis: Sì. La tua domanda è già stata posta, risolta e discussa a morte in questa pagina di follow-up
Ben Voigt,

Ahh, la pagina mobile non ha mostrato i commenti. Ad ogni modo, questo è il link molto più utile ... Le stranezze storiche del processo di standardizzazione non sono così rilevanti (a meno che tu non stia usando MSVC, che credo usi quella prima bozza).
Dennis Zickefoose,

@Dennis: penso che tu abbia ragione. Ho spostato i collegamenti attorno ad alcuni nella mia risposta.
Ben Voigt,

Purtroppo, cpp-next.com non esiste più. Per riferimento futuro si tratta di pagine salvate da web.archive.org: mossa implicita che rompe la compatibilità con le versioni precedenti e una pagina precedente con discussioni pertinenti .
Max Truxa,

6
struct x {
   x(int) {}
};

void f(auto x = 3) { }

int main() {
   f();
}

C ++ 03: valido.

C ++ 0x: error: parameter declared 'auto'


2
@Xeo: il codice è valido in C ++ 03. È un parametro con tipo struct xe senza nome.
Ben Voigt,

Speravo di catturare qualcuno. Vorrei solo che @Xeo non fosse stato così veloce nell'eliminare il suo commento, dato che non riuscivo a leggerlo!
Razze di leggerezza in orbita,

@Xeo: senza scavare nella grammatica, sono sicuro che auto non è una parola chiave valida lì. Se lo fosse, probabilmente funzionerebbe come previsto, ma è probabilmente molto difficile da definire correttamente.
Dennis Zickefoose,

Diciamo solo che mi hai beccato. Ha letteralmente ignorato la struttura. :)
Xeo,

@Tomalek: Xeo ha giustamente sottolineato che C ++ 03 non ha int implicito.
Ben Voigt,

-4

Caratteristiche del linguaggio

  1. Inizializzazione uniforme e generale utilizzando {}
  2. auto
  3. Prevenzione del restringimento
  4. constexpr
  5. Range basato su loop
  6. nullptr
  7. classe enum
  8. static_assert
  9. std :: initializer_list
  10. Riferimenti valore (sposta semantica)
  11. >>
  12. lambda
  13. Modelli variabili
  14. Alias ​​di tipo e modello
  15. Personaggi Unicode
  16. tipo intero lungo lungo
  17. alignas e alignof
  18. decltype
  19. Letterali stringa grezzi
  20. POD generalizzato
  21. Sindacati generalizzati
  22. Classi locali come argomenti modello
  23. Sintassi del tipo restituito dal suffisso
  24. [[carry_dependency]] e [[noreturn]]
  25. identificatore noexcept
  26. operatore senza eccezione.
  27. C99 caratteristiche:
    • tipi integrali estesi
    • concatenazione di stringhe strette / larghe
    • _ _ STDC_HOSTED _ _
    • _Pragma (X)
    • macro vararg e argomenti macro vuoti
  28. _ _ func _ _
  29. Spazi dei nomi incorporati
  30. Delegare i costruttori
  31. Inizializzatori membri in classe
  32. predefinito ed elimina
  33. Operatori di conversione esplicita
  34. Letterali definiti dall'utente
  35. Modelli esterni
  36. Argomenti modello predefiniti per modelli funzione
  37. Costruttori ereditari
  38. override e final
  39. Regola SFINAE più semplice e più generale
  40. Modello di memoria
  41. thread_local

Componenti della libreria standard

  1. initializer_list per contenitori
  2. Spostare la semantica per i contenitori
  3. forward_list
  4. Contenitori hash
    • unordered_map
    • unordered_multimap
    • unordered_set
    • unordered_multiset
  5. Puntatori di gestione delle risorse
    • unique_ptr
    • shared_ptr
    • weak_ptr
  6. Supporto per la concorrenza
    • filo
    • mutex
    • serrature
    • variabili di condizione
  7. Supporto di concorrenza di livello superiore
    • packaged_thread
    • futuro
    • promettere
    • async
  8. tuple
  9. regex
  10. Numeri casuali
    • uniform_int_distribution
    • distribuzione normale
    • random_engine
    • eccetera.
  11. Nomi di tipi interi, come int16_t, uint32_t e int_fast64_t
  12. Vettore
  13. Copiare e ridisegnare le eccezioni
  14. errore di sistema
  15. operazioni emplace () per i contenitori
  16. funzioni constexpr
  17. Uso sistematico di funzioni noexcept
  18. funzione e associazione
  19. Conversioni da stringa a valore numerico
  20. Allocatori con ambito
  21. Tratti di tipo
  22. Utilità temporali: durata e time_point
  23. rapporto
  24. quick_exit
  25. Altri algoritmi, come move (), copy_if () e is_sorted ()
  26. Raccolta dei rifiuti ABI
  27. Atomics

Funzionalità obsolete

  1. Generazione del costruttore di copie e assegnazione della copia per una classe con un distruttore.
  2. Assegna una stringa letterale a un carattere *.
  3. Specifica dell'eccezione C ++ 98
    • unexcepted_handler
    • set_unexpected
    • get_unexpected
    • inaspettato
  4. Oggetti funzione e funzioni associate
  5. auto_ptr
  6. Registrati
  7. ++ su un bool
  8. esportare
  9. Calchi in stile C.

3
Questo non risponde alla domanda.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.