Odpowiedź składa się z dwóch części. Zgodność na poziomie kompilatora i zgodność na poziomie konsolidatora. Zacznijmy od tego pierwszego.
załóżmy, że wszystkie nagłówki zostały napisane w C ++ 11
Użycie tego samego kompilatora oznacza, że ten sam nagłówek biblioteki standardowej i pliki źródłowe (elementy skojarzone z kompilatorem) będą używane niezależnie od docelowego standardu C ++. Dlatego pliki nagłówkowe biblioteki standardowej są napisane tak, aby były kompatybilne ze wszystkimi wersjami C ++ obsługiwanymi przez kompilator.
To powiedziawszy, jeśli opcje kompilatora użyte do kompilacji jednostki tłumaczeniowej określają konkretny standard C ++, to wszelkie funkcje, które są dostępne tylko w nowszych standardach, nie powinny być dostępne. Odbywa się to za pomocą __cplusplus
dyrektywy. Zobacz plik źródłowy wektorowy , aby zobaczyć interesujący przykład jego użycia. Podobnie kompilator odrzuci wszelkie funkcje składniowe oferowane przez nowsze wersje standardu.
Wszystko to oznacza, że twoje założenie może dotyczyć tylko napisanych przez ciebie plików nagłówkowych. Te pliki nagłówkowe mogą powodować niezgodności, jeśli są zawarte w różnych jednostkach tłumaczeniowych przeznaczonych dla różnych standardów C ++. Jest to omówione w załączniku C standardu C ++. Są 4 klauzule, omówię tylko pierwszą, a resztę krótko wspomnę.
C.3.1 Klauzula 2: konwencje leksykalne
Pojedyncze cudzysłowy ograniczają literał znaku w C ++ 11, podczas gdy są separatorami cyfr w C ++ 14 i C ++ 17. Załóżmy, że masz następującą definicję makra w jednym z czystych plików nagłówkowych C ++ 11:
#define M(x, ...) __VA_ARGS__
int x[2] = { M(1'2,3'4) };
Rozważ dwie jednostki tłumaczeniowe, które zawierają plik nagłówkowy, ale są przeznaczone odpowiednio dla C ++ 11 i C ++ 14. W przypadku języka C ++ 11 przecinek w cudzysłowie nie jest traktowany jako separator parametrów; jest tylko jeden parametr. Dlatego kod byłby równoważny z:
int x[2] = { 0 };
Z drugiej strony, gdy celujemy w C ++ 14, pojedyncze cudzysłowy są interpretowane jako separatory cyfr. Dlatego kod byłby równoważny z:
int x[2] = { 34, 0 };
Chodzi o to, że użycie pojedynczych cudzysłowów w jednym z czystych plików nagłówkowych C ++ 11 może spowodować zaskakujące błędy w jednostkach tłumaczeniowych, które są przeznaczone dla C ++ 14/17. Dlatego nawet jeśli plik nagłówkowy jest napisany w C ++ 11, musi być napisany ostrożnie, aby zapewnić zgodność z późniejszymi wersjami standardu. __cplusplus
Dyrektywa może być tu przydatna.
Pozostałe trzy klauzule standardu to:
C.3.2 Rozdział 3: podstawowe pojęcia
Zmiana : nowy zwykły (bez miejsca docelowego) dezalokator
Uzasadnienie : Wymagane w przypadku zmniejszenia przydziału.
Wpływ na oryginalną funkcję : prawidłowy kod C ++ 2011 może zadeklarować globalną funkcję alokacji miejsca docelowego i funkcję cofania alokacji w następujący sposób:
void operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
Jednak w niniejszej Normie Międzynarodowej deklaracja usunięcia operatora może odpowiadać predefiniowanemu zwykłemu operatorowi usuwania (bez umieszczania) (3.7.4). Jeśli tak, program jest źle sformułowany, jak to miało miejsce w przypadku funkcji alokacji elementów klasy i funkcji cofania alokacji (5.3.4).
C.3.3 Klauzula 7: deklaracje
Zmiana : niestatyczne funkcje składowe constexpr nie są niejawnie stałymi funkcjami składowymi.
Uzasadnienie : Jest to konieczne, aby umożliwić funkcjom składowym constexpr mutowanie obiektu.
Wpływ na oryginalną funkcję : Prawidłowy kod C ++ 2011 może nie zostać skompilowany w tym standardzie międzynarodowym.
Na przykład poniższy kod jest prawidłowy w C ++ 2011, ale nieprawidłowy w tym standardzie międzynarodowym, ponieważ dwukrotnie deklaruje tę samą funkcję składową z różnymi typami zwracanych:
struct S {
constexpr const int &f();
int &f();
};
C.3.4 Klauzula 27: biblioteka wejścia / wyjścia
Zmiana : pobieranie nie jest zdefiniowane.
Uzasadnienie : używanie gadżetów jest uważane za niebezpieczne.
Wpływ na oryginalną funkcję : Prawidłowy kod C ++ 2011 korzystający z funkcji gets może nie zostać skompilowany zgodnie z niniejszą normą międzynarodową.
Potencjalne niezgodności między C ++ 14 i C ++ 17 są omówione w C.4. Ponieważ wszystkie niestandardowe pliki nagłówkowe są napisane w C ++ 11 (jak określono w pytaniu), te problemy nie wystąpią, więc nie będę o nich tutaj wspominał.
Teraz omówię kompatybilność na poziomie konsolidatora. Ogólnie rzecz biorąc, potencjalne przyczyny niezgodności obejmują:
Jeśli format wynikowego pliku obiektowego zależy od docelowego standardu C ++, konsolidator musi mieć możliwość łączenia różnych plików obiektowych. W GCC, LLVM i VC ++ na szczęście tak nie jest. Oznacza to, że format plików obiektowych jest taki sam, niezależnie od docelowego standardu, chociaż w dużym stopniu zależy od samego kompilatora. W rzeczywistości żaden z linkerów GCC, LLVM i VC ++ nie wymaga wiedzy o docelowym standardzie C ++. Oznacza to również, że możemy łączyć pliki obiektowe, które są już skompilowane (łącząc statycznie środowisko wykonawcze).
Jeśli procedura uruchamiania programu (funkcja, która wywołuje main
) jest inna dla różnych standardów C ++ i różne procedury nie są ze sobą kompatybilne, wówczas nie byłoby możliwe dowiązanie plików obiektowych. W GCC, LLVM i VC ++ na szczęście tak nie jest. Ponadto podpis main
funkcji (i ograniczenia, które się do niej stosuje, patrz sekcja 3.6 standardu) jest taki sam we wszystkich standardach C ++, więc nie ma znaczenia, w której jednostce tłumaczeniowej istnieje.
Ogólnie WPO może nie działać dobrze z plikami obiektowymi skompilowanymi przy użyciu różnych standardów C ++. Zależy to dokładnie od tego, które etapy kompilatora wymagają znajomości docelowego standardu, a które nie, oraz od wpływu, jaki ma on na optymalizacje międzyprocedurowe, które krzyżują się z plikami obiektowymi. Na szczęście GCC, LLVM i VC ++ są dobrze zaprojektowane i nie mają tego problemu (nie jestem tego świadomy).
Dlatego GCC, LLVM i VC ++ zostały zaprojektowane tak, aby umożliwić zgodność binarną z różnymi wersjami standardu C ++. Nie jest to jednak wymóg samej normy.
Nawiasem mówiąc, chociaż kompilator VC ++ oferuje przełącznik std , który umożliwia kierowanie na określoną wersję standardu C ++, nie obsługuje kierowania na C ++ 11. Minimalną wersją, którą można określić, jest C ++ 14, która jest domyślną wersją począwszy od Visual C ++ 2013 Update 3. Możesz użyć starszej wersji VC ++ do kierowania na C ++ 11, ale wtedy będziesz musiał użyć innych kompilatorów VC ++ kompilowanie różnych jednostek tłumaczeniowych, które są przeznaczone dla różnych wersji standardu C ++, co co najmniej złamałoby WPO.
UWAGA: Moja odpowiedź może nie być kompletna lub bardzo precyzyjna.