Jakie przełomowe zmiany wprowadzono w C ++ 11?


227

Wiem, że przynajmniej jedna ze zmian w C ++ 11, która spowoduje, że jakiś stary kod przestanie się kompilować: wprowadzenie explicit operator bool()do standardowej biblioteki, zastępujące stare instancje operator void*(). To prawda, że ​​kod, który to zepsuje, jest prawdopodobnie kodem, który nie powinien był być poprawny, ale nadal jest przełomową zmianą: programy, które kiedyś były prawidłowe, już nie są.

Czy są jakieś inne przełomowe zmiany?


1
Usunąć znaczenie exportsłowa kluczowego? Zdobędę płaszcz.
Steve Jessop,

7
Wiesz, nie nazwałbym tego zamianą konwersji na bool „przełomową zmianą” ... bardziej jak „karną zmianą”.
Xeo

4
Kiedy wszystkie formalności potrzebne do stworzenia takiego związku tylko czekają na pieczątkę, jasne, dlaczego nie?
Dennis Zickefoose

3
@Xeo: mystream.good()to nie to samo co bool(mystream)? good()jest prawdziwe, jeśli nie ustawiono żadnej flagi. bool(mystream)jest nadal fałszywe, jeśli tylko eofbitjest ustawione. !mystream.fail()byłby prawidłowym odpowiednikiem.
R. Martinho Fernandes,

2
Uwaga moderatora : „ Zachowaj komentarze na dany temat, mając pod ręką pytanie lub odpowiedź. Dyskutując pytanie lub odpowiedź, dyskusja powinna dotyczyć tylko tego, pytania lub odpowiedzi pod ręką. Dyskusja zasadniczo nie jest konstruktywna w przypadku przepełnienia stosu. Antagonizacja z pewnością nie jest.
Tim Post

Odpowiedzi:


178

FDIS zawiera sekcję dotyczącą niezgodności w dodatku C.2„C ++ i ISO C ++ 2003”.

Podsumowanie, parafrazując tutaj FDIS, aby uczynić go (lepszym) odpowiednim jako odpowiedź SO. Dodałem kilka własnych przykładów, aby zilustrować różnice.

Istnieje kilka niezgodności związanych z biblioteką, w których nie znam dokładnie konsekwencji, więc pozostawiam je innym do rozwinięcia.

Podstawowy język


#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 .

Nowe słowa kluczowe: alignas, alignof, char16_t, char32_t, constexpr, decltype, noexcept, nullptr, static_assert i thread_local


Niektóre literały całkowite większe niż mogą być reprezentowane przez długie, mogą zmienić się z typu całkowitego bez znaku na znak długi.


Prawidłowy kod C ++ 2003, który używa dzielenia liczb całkowitych, zaokrągla wynik w kierunku 0 lub w kierunku ujemnej nieskończoności, podczas gdy C ++ 0x zawsze zaokrągla wynik w kierunku 0.

(co prawda nie jest tak naprawdę problemem kompatybilności dla większości ludzi).


Prawidłowy kod C ++ 2003, który używa słowa kluczowego autojako specyfikatora klasy pamięci, może być niepoprawny w C ++ 0x.


Zwężenie konwersji powoduje niezgodność z C ++ 03. Na przykład poniższy kod jest poprawny w C ++ 2003, ale jest niepoprawny w tym standardzie międzynarodowym, ponieważ podwójna int to konwersja zawężająca:

int x[] = { 2.0 };

Specjalnie zadeklarowane funkcje specjalne są definiowane jako usunięte, gdy niejawna definicja byłaby źle sformułowana.

Prawidłowy program C ++ 2003, który używa jednej z tych specjalnych funkcji składowych w kontekście, w którym definicja nie jest wymagana (np. W wyrażeniu, które nie jest potencjalnie oceniane) staje się źle sformułowany.

Przykład mojego autorstwa:

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

Taki rozmiar sztuczek był używany przez niektóre SFINAE i należy go teraz zmienić :)


Destruktory zadeklarowane przez użytkownika mają domyślną specyfikację wyjątku.

Przykład mojego autorstwa:

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

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

Ten kod wywołuje terminatew C ++ 0x, ale nie w C ++ 03. Ponieważ niejawna specyfikacja wyjątku A::~Aw C ++ 0x to noexcept(true).


Prawidłowa deklaracja C ++ 2003 exportjest źle sformułowana w C ++ 0x.


Prawidłowe wyrażenie C ++ 2003 zawierające >bezpośrednio po nim kolejne >może być teraz traktowane jako zamknięcie dwóch szablonów.

W C ++ 03 >>zawsze będzie tokenem shift-operator.


Zezwalaj na zależne wywołania funkcji z wewnętrznym powiązaniem.

Przykład mojego autorstwa:

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

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

int main() { g(0); }

W C ++ 03 to wywołuje f(long), ale w C ++ 0x to wywołuje f(int). Należy zauważyć, że zarówno w C ++ 03, jak i C ++ 0x następujące wywołania f(B)(kontekst tworzenia instancji nadal uwzględnia tylko deklaracje powiązań zewnętrznych).

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); }

Lepsze dopasowanie f(A)nie jest podejmowane, ponieważ nie ma powiązania zewnętrznego.


Zmiany w bibliotece

Prawidłowy kod C ++ 2003, który używa dowolnych identyfikatorów dodanych do standardowej biblioteki C ++ C ++ 0x, może nie zostać skompilowany lub wygenerować inne wyniki w niniejszym standardzie międzynarodowym.


Poprawny kod C ++ 2003, który #includesnagłówki z nazwami nowych nagłówków biblioteki standardowej C ++ 0x mogą być niepoprawne w tym standardzie międzynarodowym.


<algorithm>Zamiast tego może zostać dołączony prawidłowy kod C ++ 2003, który został skompilowany z oczekiwaniem na zamianę<utility>


Globalna przestrzeń nazw posixjest teraz zarezerwowana do standaryzacji.


Poprawny kod C ++ 2003, który definiuje override, final, carries_dependency, lub noreturnjako makra jest nieprawidłowy w C ++ 0x.


„Zezwalaj na zależne wywołania funkcji z wewnętrznym powiązaniem”. Czy mógłbyś wyjaśnić różnicę między Twoimi dwoma przykładami? Najwyraźniej czegoś mi brakuje.
Dennis Zickefoose

@Dennis zmiana została wprowadzona przez open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#561 . Chociaż nie komentują tego faktu, „kontekst tworzenia instancji” nadal składa się tylko z „zestawu deklaracji z zewnętrznym powiązaniem zadeklarowanym przed momentem wystąpienia specjalizacji szablonu w tej samej jednostce tłumaczeniowej”. Wprowadzona przez nich zmiana wpływa tylko na wyszukiwanie w kontekście definicji.
Johannes Schaub - litb

W pierwszym podanym przykładzie funkcja powiązania wewnętrznego była widoczna i znaleziona w kontekście definicji szablonu. W moim drugim przykładzie wewnętrzna funkcja powiązania musiałaby być częścią kontekstu tworzenia wystąpienia, aby ją znaleźć. Ale ponieważ tak nie jest, nie można go znaleźć.
Johannes Schaub - litb

Nawiasem mówiąc, myślę, że jedynym przypadkiem, w którym dla kontekstu definicji szablonu można bezpiecznie znaleźć funkcję z wewnętrznym powiązaniem, jest to, gdy specjalizacja szablonu funkcji jest wyraźnie utworzona w jednej instancji JT (gdzie szablon jest zdefiniowany), a wszystkie inne JT polegają na ta wyraźna instancja. We wszystkich innych przypadkach (w których inne jednostki językowe same stworzyłyby specjalizację) naruszyłbyś ODR, używając definicji szablonu używającej za każdym razem innej funkcji (powiązanie wewnętrzne).
Johannes Schaub - litb

Nie jestem więc pewien, dlaczego zachowali ograniczenie kontekstu tworzenia instancji - istniałaby tylko jedna (wyraźna) instancja, a ta instancja używałaby wewnętrznych funkcji łączenia znalezionych w kontekście tworzenia instancji TU. Podobnie jak w przypadku definicji. Nawiasem mówiąc, myślę, że gdybyśmy nadal mieli export, to sądzę, że inne jednostki językowe nie musiałyby polegać na jawnej instancji, ale same mogłyby utworzyć instancję szablonu. Wtedy miałoby to znaczenie, czy wewnętrzne funkcje łączenia są widoczne w kontekście tworzenia.
Johannes Schaub - litb

28

Znaczenie słowa kluczowego auto uległo zmianie.


9
Jeśli używasz autosłowa kluczowego, coś jest bardzo nie tak z twoim kodem. Dlaczego do licha miałbyś go używać?
Elazar Leibovich,

To nie jest przełomowa zmiana . Każde prawidłowe użycie C ++ 03 autopozostaje ważne w C ++ 11.
Drew Dormann

11
@DrewDormann int main() { auto int i = 0; return i; }jest całkowicie poprawny w C ++ 03, ale błąd składniowy w C ++ 11. Jedynym ostrzeżeniem, które mogę uzyskać od kompilatorów w trybie C ++ 03, jest ostrzeżenie o zgodności.

24

Łamać zmiany?

Cóż, z jednej strony, jeśli jest używana decltype, constexpr, nullptr, itd. Jako identyfikatory wtedy może mieć kłopoty ...


21

Niektóre podstawowe niezgodności, które nie są objęte sekcją dotyczącą niezgodności:


C ++ 0x traktuje nazwę wstrzykniętej klasy jako szablon, jeśli nazwa jest przekazywana jako argument parametru szablonu szablonu i jako typ, jeśli jest przekazywana do parametru typu szablonu.

Poprawny kod C ++ 03 może zachowywać się inaczej, jeśli opiera się na wstrzykiwanej nazwie klasy, aby zawsze był typem w tych scenariuszach. Przykładowy kod zaczerpnięty z mojego 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();
}

W C ++ 03 kod wywołuje drugi za gkażdym razem.


C ++ 0x sprawia, że ​​niektóre nazwy zależne w C ++ 03 są teraz niezależne. I wymaga wyszukiwania nazw dla niezależnych nazw kwalifikowanych, które odnoszą się do członków bieżącego szablonu klasy, które muszą być powtarzane przy tworzeniu, i wymaga sprawdzenia, czy te nazwy wyszukują w taki sam sposób, jak w kontekście definicji szablonu.

Prawidłowy kod C ++ 03, który zależy od reguły dominacji, może teraz nie być kompilowany z powodu tej zmiany.

Przykład:

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(); }

Ten poprawny kod C ++ 03, który wywołuje, A<int>::fnie jest poprawny w C ++ 0x, ponieważ wyszukiwanie nazw podczas tworzenia instancji zostanie znalezione A<int>::fw przeciwieństwie do B::f, co powoduje konflikt z wyszukiwaniem w definicji.

W tym momencie nie jest jasne, czy jest to wada FDIS. Komitet jest tego świadomy i oceni sytuację.


Deklaracja za pomocą, w której ostatnia część jest taka sama jak identyfikator w ostatniej części kwalifikatora w nazwie kwalifikowanej oznaczającej klasę podstawową, która za pomocą deklaracji nazywa teraz konstruktor, a nie członków o tej nazwie.

Przykład:

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; }

Powyższy przykładowy kod jest dobrze sformułowany w C ++ 03, ale źle sformułowany w C ++ 0x, ponieważ A::Bnadal jest niedostępny main.


14

Błąd ekstrakcji strumienia jest traktowany inaczej.

Przykład

#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
}

Zmień propozycję

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

Standardowe odniesienie

[C++03: 22.2.2.1.2/11]: Wynik przetwarzania etapu 2 może być jednym z

  • Sekwencja znaków została zgromadzona na etapie 2, który jest konwertowany (zgodnie z regułami scanf) na wartość typu val. Ta wartość jest przechowywana w vali ios_base::goodbitjest przechowywana w err.
  • Sekwencja znaków zebranych w etapie 2 spowodowałaby scanfzgłoszenie awarii wejścia. ios_base::failbitjest przypisany do err. [ed .: Nic nie jest przechowywane val.]

[C++11: 22.4.2.1.2/3]: [..] Zapisywana wartość liczbowa może być jedną z:

  • zero, jeśli funkcja konwersji nie skonwertuje całego pola . ios_base::failbitjest przypisany do err.
  • najbardziej dodatnia reprezentowalna wartość, jeśli pole reprezentuje wartość zbyt dużą dodatnią, aby można ją było przedstawić w val. ios_base::failbitjest przypisany do err.
  • najbardziej ujemna reprezentowalna wartość lub zero dla typu liczby całkowitej bez znaku, jeśli pole reprezentuje wartość zbyt dużą ujemną, aby można ją było przedstawić w val. ios_base::failbitjest przypisany do err.
  • w przeciwnym razie przeliczona wartość.

Wynikowa wartość liczbowa jest przechowywana w val.

Realizacje

  • GCC 4.8 poprawnie wyprowadza dla C ++ 11 :

    Asercja `x == -1 'nie powiodła się

  • GCC 4.5-4.8 wszystkie dane wyjściowe dla C ++ 03 następujące, które wydają się być błędem:

    Asercja `x == -1 'nie powiodła się

  • Visual C ++ 2008 Express poprawnie wyświetla dane wyjściowe dla C ++ 03:

    Potwierdzenie nie powiodło się: x == 0

  • Visual C ++ 2012 Express niepoprawnie wyświetla dane wyjściowe dla C ++ 11, co wydaje się być problemem związanym ze statusem wdrożenia:

    Potwierdzenie nie powiodło się: x == 0


13

W jaki sposób wprowadzenie jawnych operatorów konwersji jest przełomową zmianą? Stara wersja nadal będzie tak samo „ważna” jak poprzednio.

Tak, zmiana z operator void*() constna explicit operator bool() constbędzie przełomową, ale tylko jeśli zostanie użyta w sposób, który jest niewłaściwy sam w sobie. Zgodny kod nie zostanie uszkodzony.

Kolejną przełomową zmianą jest zakaz zawężania konwersji podczas inicjalizacji zbiorczej :

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

Edycja : Tylko pamięć, std::identity<T>zostanie usunięty w C ++ 0x (patrz uwaga). Struktura wygody pozwala uzależnić typy. Ponieważ struct naprawdę niewiele robi, powinno to naprawić:

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

Jeśli do standardowych obiektów biblioteki dodano jawne konwersje, istniejące niejawne konwersje mogą przestać działać. Ale nie wyobrażam sobie scenariusza, w którym konwersja nie byłaby ważna i nie zrobiłbym czegoś pożytecznego.
Dennis Zickefoose

Wprowadzenie jest przełomową zmianą, ponieważ zastąpi istniejące operator void*.
R. Martinho Fernandes,

@Dennis: Aaah, teraz rozumiem, co @Martinho miał na myśli. Ale będzie to przełomowa zmiana, jeśli ludzie użyją jej w sposób inny niż zamierzony.
Xeo

„ale tylko wtedy, gdy jest używany w sposób, który jest niewłaściwy sam w sobie” - bool ok = cin >> a; cout << "done reading" << endl; if (ok) { ... }Nie ma w tym nic złego w C ++ 03, ale stał się błędem w C ++ 11. (Uwaga: GCC 4.9 wciąż operator void*() consttu jest, dlatego akceptuje kod w trybie C ++ 11.)

std::identity<T>nie został usunięty w C ++ 11, ponieważ nie był częścią C ++ 03. Krótko istniał w projekcie dla C ++ 11 i został usunięty z projektu przed standaryzacją.
Howard Hinnant,


7

Było wiele dyskusji na temat niejawnego łamania kompatybilności wstecznej

( starsza strona z odpowiednią dyskusją )

Jeśli przeczytasz w komentarzach, niejawny zwrot ruchu jest również przełomową zmianą.


Rezultatem tych dyskusji jest to, że został usunięty w prawie wszystkich przypadkach. Czy są jakieś problemy z tym, co zostało?
Dennis Zickefoose

@Dennis: Tak. Twoje pytanie zostało już zadane, udzielone i omówione na śmierć na następnej stronie
Ben Voigt,

Ahh, strona mobilna nie wyświetlała komentarzy. Tak czy inaczej, jest to o wiele bardziej użyteczny link ... Historyczne osobliwości procesu standaryzacji nie są tak istotne (chyba że używasz MSVC, który, jak sądzę, używa tego pierwszego szkicu).
Dennis Zickefoose

@Dennis: Myślę, że masz rację. Przesunęłam niektóre linki w mojej odpowiedzi.
Ben Voigt,

Niestety, cpp-next.com już nie istnieje. Do przyszłego wykorzystania są to strony zapisane przez web.archive.org: niejawny ruch łamający wsteczną kompatybilność oraz starsza strona z odpowiednią dyskusją .
Max Truxa

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

void f(auto x = 3) { }

int main() {
   f();
}

C ++ 03: poprawny.

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


2
@Xeo: Kod jest poprawny w C ++ 03. Jest to parametr typu struct xi bez nazwy.
Ben Voigt,

Miałem nadzieję, że kogoś złapię. Chciałbym tylko, żeby @Xeo nie usunął tak szybko swojego komentarza, ponieważ nie mogłem go przeczytać!
Wyścigi lekkości na orbicie

@Xeo: Bez wnikania w gramatykę jestem pewien, że auto nie jest tam prawidłowym słowem kluczowym. Gdyby tak było, prawdopodobnie działałoby to zgodnie z oczekiwaniami, ale prawdopodobnie jest to naprawdę trudne do prawidłowego zdefiniowania.
Dennis Zickefoose

Powiedzmy, że mnie złapałeś. Dosłownie zignorował strukturę. :)
Xeo

@Tomalek: Xeo słusznie zauważył, że C ++ 03 nie ma ukrytego int.
Ben Voigt

-4

Funkcje językowe

  1. Jednolita i ogólna inicjalizacja za pomocą {}
  2. automatyczny
  3. Zapobieganie zwężeniu
  4. constexpr
  5. Zasięg oparty na pętli
  6. nullptr
  7. klasa enum
  8. static_assert
  9. std :: initializer_list
  10. Odwołania do wartości (semantyka przenoszenia)
  11. >>
  12. Lambdas
  13. Szablony Variadic
  14. Aliasy typu i szablonu
  15. Znaki Unicode
  16. długa długa liczba całkowita
  17. alignas i alignof
  18. decltype
  19. Surowe literały łańcuchowe
  20. Uogólniony POD
  21. Uogólnione związki zawodowe
  22. Klasy lokalne jako argumenty szablonu
  23. Składnia typu zwracanego sufiksu
  24. [[carries_dependency]] i [[noreturn]]
  25. specyfikator noexcept
  26. noexcept operator.
  27. Funkcje C99:
    • rozszerzone typy całek
    • konkatenacja wąskiego / szerokiego sznurka
    • _ _ STDC_HOSTED _ _
    • _Pragma (X)
    • makra vararg i puste argumenty makr
  28. _ _ func _ _
  29. Wbudowane przestrzenie nazw
  30. Delegowanie konstruktorów
  31. Inicjatory elementów w klasie
  32. domyślne i usuń
  33. Jawne operatory konwersji
  34. Literały zdefiniowane przez użytkownika
  35. Szablony zewnętrzne
  36. Domyślne argumenty szablonu dla szablonów funkcji
  37. Dziedziczenie konstruktorów
  38. zastąpienie i ostateczne
  39. Prostsza i ogólniejsza zasada SFINAE
  40. Model pamięci
  41. Wątek_lokalny

Komponenty biblioteki standardowej

  1. lista_inicjalizatora dla kontenerów
  2. Przenieś semantykę dla kontenerów
  3. forward_list
  4. Pojemniki z mieszaniem
    • unordered_map
    • unordered_multimap
    • unordered_set
    • unordered_multiset
  5. Wskaźniki zarządzania zasobami
    • Unique_ptr
    • shared_ptr
    • słaby_ptr
  6. Obsługa współbieżności
    • wątek
    • muteksy
    • zamki
    • zmienne warunkowe
  7. Obsługa współbieżności na wyższym poziomie
    • packaged_thread
    • przyszłość
    • obietnica
    • asynchronizacja
  8. krotki
  9. regex
  10. Losowe liczby
    • uniform_int_distribution
    • normalna dystrybucja
    • silnik_ losowy
    • itp.
  11. Nazwy typów całkowitych, takie jak int16_t, uint32_t i int_fast64_t
  12. szyk
  13. Kopiowanie i ponowne zgłaszanie wyjątków
  14. błąd systemu
  15. Operacje Situace () dla kontenerów
  16. funkcje constexpr
  17. Systematyczne korzystanie z funkcji noexcept
  18. funkcja i powiązanie
  19. Konwersje ciągów na wartości liczbowe
  20. Przydziały z zakresem
  21. Wpisz cechy
  22. Narzędzia czasu: czas trwania i punkt_czasu
  23. stosunek
  24. szybkie wyjście
  25. Więcej algorytmów, takich jak move (), copy_if () i is_sorted ()
  26. Śmieciarka ABI
  27. atomics

Przestarzałe funkcje

  1. Generowanie konstruktora kopiowania i przypisanie kopii dla klasy z destruktorem.
  2. Przypisz literał ciąg znaków do znaku *.
  3. Specyfikacja wyjątku C ++ 98
    • nieoczekiwany program obsługi
    • set_u nieoczekiwany
    • get_u nieoczekiwany
    • niespodziewany
  4. Obiekty funkcji i powiązane funkcje
  5. auto_ptr
  6. zarejestrować
  7. ++ na bool
  8. eksport
  9. Odlewy w stylu C.

3
To nie odpowiada na pytanie.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.