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:
int main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
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 ):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
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:
int i = 0 ;
i = ++ i + 1;
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:
int i = 0 ;
++++i ;
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:
Specyfikator constexpr deklaruje, że możliwe jest oszacowanie wartości funkcji lub zmiennej w czasie kompilacji. Takie zmienne i funkcje mogą być następnie użyte, gdy dozwolone są tylko wyrażenia stałej czasowej kompilacji.
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:
#include <limits>
int main()
{
int x[std::numeric_limits<unsigned int>::min()+2] ;
}
Użycie clang
w C ++ 03 spowoduje, że x
będzie to tablica o zmiennej długości, która jest rozszerzeniem i wygeneruje następujące ostrzeżenie:
warning: variable length arrays are a C99 feature [-Wvla-extension]
int x[std::numeric_limits<unsigned int>::min()+2] ;
^
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:
#include <iostream>
#include <stdexcept>
struct S
{
~S() { throw std::runtime_error(""); } // bad, but acceptable
};
int main()
{
try { S s; }
catch (...) {
std::cerr << "exception occurred";
}
std::cout << "success";
}
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:
#include <iostream>
#include <vector>
#include <algorithm>
class Comparators
{
public:
bool operator()(int first, int second)
{
return first < second;
}
};
int main()
{
class ComparatorsInner : public Comparators{};
std::vector<int> compares ;
compares.push_back(20) ;
compares.push_back(10) ;
compares.push_back(30) ;
ComparatorsInner comparatorInner;
std::sort(compares.begin(), compares.end(), comparatorInner);
std::vector<int>::iterator it;
for(it = compares.begin(); it != compares.end(); ++it)
{
std::cout << (*it) << std::endl;
}
}
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 :
#include <iostream>
template<int I> struct X {
static int const c = 2;
};
template<> struct X<0> {
typedef int c;
};
template<typename T> struct Y {
static int const c = 3;
};
static int const c = 4;
int main() {
std::cout << (Y<X<1> >::c >::c>::c) << '\n';
std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}
a wynik w C ++ 03 to:
0
3
oraz w C ++ 11:
0
0
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 :
std::vector<T> test(1);
daje różne wyniki w C ++ 03 i C ++ 11:
#include <iostream>
#include <vector>
struct T
{
bool flag;
T() : flag(false) {}
T(const T&) : flag(true) {}
};
int main()
{
std::vector<T> test(1);
bool is_cpp11 = !test[0].flag;
std::cout << is_cpp11 << std::endl ;
}
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:
int x[] = { 2.0 };
Jest to omówione w projekcie standardowej sekcji C ++ 11 akapit 3 : 8.5.4
Inicjalizacja listy :
Inicjalizacja listy obiektu lub odwołania typu T jest zdefiniowana następująco:
i zawiera następujący punktor ( moje wyróżnienie ):
W przeciwnym razie, jeśli T jest typem klasy, brane są pod uwagę konstruktory. Odpowiednie konstruktory są wyliczone, a najlepszy wybiera się na podstawie rozdzielczości przeciążenia (13.3, 13.3.1.7). Jeśli do konwersji któregokolwiek z argumentów wymagane jest zawężenie konwersji (patrz poniżej), program jest źle sformułowany
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
#define u8 "abc"
const char *s = u8"def"; // Previously "abcdef", now "def"
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.
#define _x "there"
"hello"_x // #1
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.
auto
może to doprowadzić do takiej sytuacji