Czy jest możliwe, aby kod C ++ był zgodny zarówno ze standardem C ++ 03, jak i standardem C ++ 11 , ale robi różne rzeczy w zależności od tego, w ramach którego standardu jest kompilowany?
c++
c++11
language-lawyer
c++03
Erik Sjölund
źródło
źródło
auto
może to doprowadzić do takiej sytuacji>>
użycie w szablonie. Możesz wymyślić sytuację, w której można skompilować oba standardy. Innym, którego jestem pewien, że łatwo byłoby znaleźć zmiany, jest inicjalizacja.auto
może to powodować. Zgodnie ze starym znaczeniemauto
deklaracja wymaga nazwy typu; w nowym znaczeniu nazwa typu jest niedozwolona.Odpowiedzi:
Odpowiedź jest jednoznaczna: tak. Na plus jest:
Po stronie negatywnej kilka przykładów wymieniono w załączniku C normy. Mimo że jest o wiele więcej negatywnych niż pozytywnych, każde z nich jest znacznie mniej prawdopodobne.
Literały łańcuchowe
i
Wpisz konwersje 0
W C ++ 11 tylko literały są stałymi zerowymi wskaźnikami liczb całkowitych:
Zaokrąglone wyniki po dzieleniu liczb całkowitych i modulo
W C ++ 03 kompilatorowi wolno było zaokrąglać w kierunku 0 lub w kierunku ujemnej nieskończoności. W C ++ 11 obowiązkowe jest zaokrąglanie w kierunku 0
Białe spacje między nawiasami zamykającymi szablony >> vs>>
Wewnątrz specjalizacji lub instancji
>>
można zamiast tego interpretować jako przesunięcie w prawo w C ++ 03. Jest to bardziej prawdopodobne, że złamie istniejący kod: (z http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )Operator
new
może teraz zgłaszać inne wyjątki niżstd::bad_alloc
Zadeklarowane przez użytkownika destruktory mają domyślny przykład specyfikacji wyjątku z Jakie przełamujące zmiany zostały wprowadzone w C ++ 11?
size()
kontenerów jest teraz wymagane do działania w O (1)std::ios_base::failure
nie pochodzistd::exception
już bezpośrednioPodczas gdy bezpośrednia klasa podstawowa jest nowa,
std::runtime_error
nie jest. A zatem:źródło
noexecpt(true)
więcthrow
w destruktorze będzie teraz wywoływałstd::terminate
. Ale mam nadzieję, że każdy, kto napisał taki kod, będzie z tego zadowolony!catch (std::exception &)
nadal się łapiestd::ios_base::failure
.operator new
jest dokładne (może teraz rzucaćstd::bad_array_new_length
), ale twój przykład wcale tego nie pokazuje. Wyświetlany kod jest taki sam w C ++ 03 i C ++ 11 AFAIK.Zwracam uwagę na ten artykuł i kontynuację , która zawiera ładny przykład tego, jak
>>
zmienić znaczenie z C ++ 03 na C ++ 11, jednocześnie kompilując oba.Kluczową częścią jest linia wejściowa
main
, która jest wyrażeniem.W C ++ 03:
W C ++ 11
Gratulacje, dwa różne wyniki dla tego samego wyrażenia. To prawda, że C ++ 03 wymyślił ostrzeżenie od Clanga, kiedy go testowałem.
źródło
typename
do::two
w C ++ 03 wersjitrue
lubfalse
dla nich. Może moglibyśmy użyć go jako testu funkcji </joke>warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
), ale wciąż jest to dobry przykład tego, jak niejednoznaczny::
operator zmienia znaczenie (albo odnosząc się do zakresu globalnego, albo dereferencjonując tego, który stoi bezpośrednio przed nim)Tak, istnieje wiele zmian, które spowodują, że ten sam kod spowoduje różne zachowanie między C ++ 03 a C ++ 11. Różnice w regułach sekwencjonowania powodują, że niektóre interesujące zmiany, w tym niektóre niezdefiniowane zachowania, stają się dobrze zdefiniowane.
1. wiele mutacji tej samej zmiennej na liście inicjalizacyjnej
Jeden bardzo interesujący przypadek narożny zawierałby wiele mutacji tej samej zmiennej na liście inicjalizacyjnej, na przykład:
Zarówno w C ++ 03, jak i C ++ 11 jest to dobrze określone, ale kolejność oceny w C ++ 03 jest nieokreślona, ale w C ++ 11 są one oceniane w kolejności, w jakiej występują . Jeśli więc skompilujemy
clang
w trybie C ++ 03, wyświetli się następujące ostrzeżenie ( zobacz na żywo ):ale nie zapewnia ostrzeżenia w C ++ 11 ( zobacz na żywo ).
2. Nowe reguły sekwencjonowania sprawiają, że i = ++ i + 1; dobrze zdefiniowany w C ++ 11
Nowe reguły sekwencjonowania przyjęte po C ++ 03 oznaczają, że:
nie jest już niezdefiniowanym zachowaniem w C ++ 11, jest to opisane w raporcie o defektach 637. Reguły sekwencjonowania i przykład nie zgadzają się
3. Nowe reguły sekwencjonowania sprawiają, że ++++ i; dobrze zdefiniowany w C ++ 11
Nowe reguły sekwencjonowania przyjęte po C ++ 03 oznaczają, że:
nie jest już niezdefiniowanym zachowaniem w C ++ 11.
4. Nieco bardziej rozsądne podpisane lewe przesunięcia
Późniejsze wersje C ++ 11,
N3485
które poniżej łączę, naprawiły niezdefiniowane zachowanie przesunięcia 1 bitu do lub powyżej bitu znaku . Jest to również uwzględnione w raporcie wad 1457 . Howard Hinnant skomentował znaczenie tej zmiany w wątku Czy przesuwanie w lewo (<<) jest zachowaniem ujemnej liczby całkowitej w C ++ 11? .5. Funkcje constexpr mogą być traktowane jako wyrażenia stałej czasowej kompilacji w C ++ 11
C ++ 11 wprowadził funkcje constexpr, które:
podczas gdy C ++ 03 nie ma funkcji constexpr , nie musimy jawnie używać słowa kluczowego constexpr, ponieważ standardowa biblioteka udostępnia wiele funkcji w C ++ 11 jako constexpr . Na przykład std :: numeric_limits :: min . Co może prowadzić do różnych zachowań, na przykład:
Użycie
clang
w C ++ 03 spowoduje, żex
będzie to tablica o zmiennej długości, która jest rozszerzeniem i wygeneruje następujące ostrzeżenie:podczas gdy w C ++ 11
std::numeric_limits<unsigned int>::min()+2
jest wyrażeniem stałej czasowej kompilacji i nie wymaga rozszerzenia VLA.6. W C ++ 11 specyfikacje wyjątków nie są generowane domyślnie dla twoich destruktorów
Ponieważ w C ++ 11 zdefiniowany przez użytkownika destruktor ma niejawną
noexcept(true)
specyfikację, jak wyjaśniono w noexcept destruktorach , oznacza to, że następujący program:W C ++ 11 zadzwoni,
std::terminate
ale będzie działać poprawnie w C ++ 03.7. W C ++ 03 argumenty szablonu nie mogą mieć wewnętrznego powiązania
Ładnie to opisano w Dlaczego std :: sort nie akceptuje Porównaj klasy zadeklarowane w funkcji . Poniższy kod nie powinien działać w C ++ 03:
ale obecnie
clang
zezwala na ten kod w trybie C ++ 03 z ostrzeżeniem, chyba że użyjesz-pedantic-errors
flagi, co jest dość trudne, zobacz na żywo .8. >> nie jest już źle sformułowany przy zamykaniu wielu szablonów
Używanie
>>
do zamykania wielu szablonów nie jest już źle sformułowane, ale może prowadzić do kodu z różnymi wynikami w C ++ 03 i C + 11. Poniższy przykład pochodzi z nawiasów kątowych i kompatybilności wstecznej :a wynik w C ++ 03 to:
oraz w C ++ 11:
9. C ++ 11 zmienia niektóre konstruktory std :: vector
Nieznacznie zmodyfikowany kod z tej odpowiedzi pokazuje, że użycie następującego konstruktora z std :: vector :
daje różne wyniki w C ++ 03 i C ++ 11:
10. Zawężanie konwersji w zbiorczych inicjalizatorach
W C ++ 11 zawężająca się konwersja inicjatorów agregujących jest źle sformułowana i wygląda na to, że
gcc
pozwala na to zarówno w C ++ 11, jak i C ++ 03, chociaż domyślnie wyświetla ostrzeżenie w C ++ 11:Jest to omówione w projekcie standardowej sekcji C ++ 11 akapit 3 :
8.5.4
Inicjalizacja listy :i zawiera następujący punktor ( moje wyróżnienie ):
To i wiele więcej instancji są uwzględnione w projekcie C ++ standardowej sekcji
annex C.2
C ++ i C ++ ISO 2003 . Obejmuje również:Nowe rodzaje literałów ciągów [...] W szczególności makra o nazwach R, u8, u8R, u, uR, U, UR lub LR nie będą rozszerzane, gdy sąsiadują z literałem ciągów, ale będą interpretowane jako część literału ciągów . Na przykład
Zdefiniowana przez użytkownika obsługa literałów [...] Poprzednio nr 1 składał się z dwóch oddzielnych tokenów przetwarzania wstępnego, a makro _x byłoby rozwinięte. W tym standardzie międzynarodowym nr 1 składa się z pojedynczych tokenów przetwarzania wstępnego, więc makro nie jest rozwijane.
Określ zaokrąglenie dla wyników liczb całkowitych / i% [...] 2003, które wykorzystują dzielenie liczb całkowitych, zaokrągla wynik w kierunku 0 lub w kierunku ujemnej nieskończoności, podczas gdy ten Międzynarodowy Standard zawsze zaokrągla wynik w kierunku 0.
Złożoność funkcji składowych size () jest teraz stała [...] Niektóre implementacje kontenerów zgodne z C ++ 2003 mogą nie spełniać określonych wymagań size () w niniejszej Normie Międzynarodowej. Dostosowanie kontenerów takich jak std :: list do surowszych wymagań może wymagać niezgodnych zmian.
Zmień klasę podstawową std :: ios_base :: awaria [...] std :: ios_base :: awaria nie jest już uzyskiwana bezpośrednio ze std :: wyjątku, ale teraz pochodzi od std :: system_error, który z kolei pochodzi od std :: runtime_error. Prawidłowy kod C ++ 2003, który zakłada, że błąd std :: ios_base :: wywodzi się bezpośrednio ze std :: wyjątek, może działać inaczej w tym standardzie międzynarodowym.
źródło
Jedną potencjalnie niebezpieczną niezgodną wstecz wsteczną zmianą są konstruktory kontenerów sekwencji, takie jak
std::vector
przeciążenie określające rozmiar początkowy. Tam, gdzie w C ++ 03, skopiowali domyślnie skonstruowany element, w C ++ 11 domyślnie konstruują każdy z nich.Rozważ ten przykład (używając
boost::shared_ptr
poprawnego C ++ 03):C ++ 03 Przykład na żywo
C ++ 11 Przykład na żywo
Powodem jest to, że C ++ 03 określił jedno przeciążenie zarówno dla „określ rozmiar, jak i element prototypowy” i „określ tylko rozmiar”, jak poniżej (argumenty alokatora pominięto dla zwięzłości):
To zawsze będzie kopiowane
prototype
do czasów kontenerasize
. Po wywołaniu z jednym argumentem utworzysize
kopie domyślnie zbudowanego elementu.W C ++ 11 ta sygnatura konstruktora została usunięta i zastąpiona tymi dwoma przeciążeniami:
Drugi działa jak poprzednio, tworząc
size
kopieprototype
elementu. Jednak pierwszy (który obsługuje teraz wywołania tylko z podanym argumentem size) domyślnie konstruuje każdy element osobno.Domyślam się, że powodem tej zmiany jest to, że przeciążenie C ++ 03 nie byłoby możliwe w przypadku elementu typu tylko ruch. Niemniej jednak jest to przełomowa zmiana i rzadko ją udokumentowano.
źródło
deque
przechowywanie dziesięciu osobnych widgetów, a nie dziesięciu widgetów współużytkujących ten sam zasób.Wynik nieudanego odczytu z
std::istream
zmienił się. CppReference ładnie to podsumowuje:Jest to przede wszystkim problem, jeśli jesteś przyzwyczajony do nowej semantyki, a następnie musisz pisać za pomocą C ++ 03. Następujące nie jest szczególnie dobrą praktyką, ale dobrze zdefiniowane w C ++ 11:
Jednak w C ++ 03 powyższy kod używa niezainicjowanej zmiennej, a zatem ma niezdefiniowane zachowanie.
źródło
int x = 1, y = 1; cin >> x >> y; cout << x*y;
. W przypadku C ++ 03 poprawnie wygenerowałoby się,x
gdyby niey
można było odczytać.Wątek Jakie ewentualne różnice między C ++ 03 a C ++ 0x, które można wykryć w czasie wykonywania, zawiera przykłady (skopiowane z tego wątku) w celu ustalenia różnic językowych, na przykład poprzez wykorzystanie zwinięcia referencji C ++ 11:
oraz c ++ 11 zezwalający na typy lokalne jako parametry szablonu:
źródło
Oto inny przykład:
Wydruki:
Zobacz wynik na Coliru
źródło