Il y a deux parties à la réponse. La compatibilité au niveau du compilateur et la compatibilité au niveau de l'éditeur de liens. Commençons par la première.
supposons que tous les en-têtes aient été écrits en C++11
L'utilisation du même compilateur signifie que les mêmes fichiers d'en-tête et sources de la bibliothèque standard (les onces associées au compilateur) seront utilisés quelle que soit la norme C++ cible. Par conséquent, les fichiers d'en-tête de la bibliothèque standard sont écrits pour être compatibles avec toutes les versions de C++ supportées par le compilateur.
Cela dit, si les options du compilateur utilisées pour compiler une unité de traduction spécifient une norme C++ particulière, les fonctionnalités qui ne sont disponibles que dans les normes plus récentes ne doivent pas être accessibles. Ceci est fait en utilisant l'option __cplusplus
directive. Voir la vecteur pour un exemple intéressant de son utilisation. De même, le compilateur rejettera toute caractéristique syntaxique offerte par des versions plus récentes de la norme.
Tout cela signifie que votre hypothèse ne peut s'appliquer qu'aux fichiers d'en-tête que vous avez écrits. Ces fichiers d'en-tête peuvent provoquer des incompatibilités lorsqu'ils sont inclus dans différentes unités de traduction visant des normes C++ différentes. Ce point est abordé dans l'annexe C de la norme C++. Il y a 4 clauses, je ne discuterai que de la première, et mentionnerai brièvement les autres.
C.3.1 Clause 2 : conventions lexicales
Les guillemets simples délimitent un littéral de caractère en C++11, alors qu'ils sont des séparateurs de chiffres en C++14 et C++17. Supposons que vous ayez la définition de macro suivante dans l'un des fichiers d'en-tête C++11 purs :
#define M(x, ...) __VA_ARGS__
// Maybe defined as a field in a template or a type.
int x[2] = { M(1'2,3'4) };
Considérons deux unités de traduction qui incluent le fichier d'en-tête, mais qui ciblent respectivement C++11 et C++14. En ciblant C++11, la virgule entre les guillemets n'est pas considérée comme un séparateur de paramètres ; il n'y a qu'un seul paramètre. Par conséquent, le code serait équivalent à :
int x[2] = { 0 }; // C++11
Par contre, en ciblant C++14, les guillemets simples sont interprétés comme des séparateurs de chiffres. Par conséquent, le code serait équivalent à :
int x[2] = { 34, 0 }; // C++14 and C++17
Le point ici est que l'utilisation de guillemets simples dans l'un des fichiers d'en-tête C++11 purs peut entraîner des bogues surprenants dans les unités de traduction qui ciblent C++14/17. Par conséquent, même si un fichier d'en-tête est écrit en C++11, il doit être écrit avec soin pour s'assurer qu'il est compatible avec les versions ultérieures de la norme. Le site __cplusplus
peut être utile ici.
Les trois autres clauses de la norme sont les suivantes :
C.3.2 Clause 3 : concepts de base
Changement : Nouveau désalloueur habituel (non-placement)
Justification : Requis pour la désallocation de taille.
Effet sur la caractéristique originale : Un code C++2011 valide pourrait déclarer une fonction d'allocation de placement global et une fonction de désallocation comme suit :
void operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
Dans la présente Norme internationale, cependant, la déclaration de l'opérateur delete peut correspondre à un opérateur delete habituel prédéfini (non-placement) (3.7.4). Si c'est le cas, le programme est mal formé, comme c'était le cas pour les fonctions d'allocation de membres de classe et les fonctions de transaction. et les fonctions de désallocation (5.3.4).
C.3.3 Clause 7 : déclarations
Changement Les fonctions membres non statiques constexpr ne sont pas des fonctions membres implicitement constantes. des fonctions membres.
Justification : Nécessaire pour permettre aux fonctions membres constexpr de muter l'objet. l'objet.
Effet sur la caractéristique originale : Le code C++2011 valide peut échouer à compiler dans ce norme internationale.
Par exemple, le code suivant est valide en C++2011 mais invalide en cette Norme Internationale car il déclare le même membre deux fois avec des types de retour différents :
struct S {
constexpr const int &f();
int &f();
};
C.3.4 Clause 27 : bibliothèque d'entrées/sorties
Changement : n'est pas défini.
Justification : L'utilisation du gets est considérée comme dangereuse.
Effet sur la caractéristique originale : Le code C++2011 valide qui utilise la fonction gets peut échouer à compiler dans cette norme internationale.
Les incompatibilités potentielles entre C++14 et C++17 sont discutées en C.4. Puisque tous les fichiers d'en-tête non standard sont écrits en C++11 (comme spécifié dans la question), ces problèmes ne se produiront pas, donc je ne les mentionnerai pas ici.
Maintenant, je vais discuter de la compatibilité au niveau du linker. En général, les raisons potentielles des incompatibilités sont les suivantes :
Si le format du fichier objet résultant dépend du standard C++ cible, l'éditeur de liens doit être capable de lier les différents fichiers objets. Dans GCC, LLVM et VC++, ce n'est heureusement pas le cas. C'est-à-dire que le format des fichiers objets est le même quel que soit le standard cible, bien qu'il dépende fortement du compilateur lui-même. En fait, aucun des linkers de GCC, LLVM et VC++ ne nécessite de connaître la norme C++ cible. Cela signifie également que nous pouvons lier des fichiers d'objets qui sont déjà compilés (liaison statique du runtime).
Si la routine de démarrage du programme (la fonction qui appelle main
) est différente pour les différents standards C++ et les différentes routines ne sont pas compatibles entre elles, alors il ne serait pas possible de lier les fichiers objets. Dans GCC, LLVM et VC++, ce n'est heureusement pas le cas. De plus, la signature de l'objet main
(et les restrictions qui s'y appliquent, voir la section 3.6 de la norme) est la même dans toutes les normes C++, donc peu importe dans quelle unité de traduction elle existe.
En général, la WPO peut ne pas fonctionner correctement avec des fichiers d'objets compilés selon des normes C++ différentes. Cela dépend exactement des étapes du compilateur qui nécessitent la connaissance de la norme cible et de celles qui ne le font pas, et de l'impact que cela a sur les optimisations interprocédurales qui traversent les fichiers objets. Heureusement, GCC, LLVM et VC++ sont bien conçus et n'ont pas ce problème (pas à ma connaissance).
C'est pourquoi GCC, LLVM et VC++ ont été conçus de manière à permettre binaire la compatibilité entre les différentes versions de la norme C++. Ce n'est cependant pas vraiment une exigence de la norme elle-même.
D'ailleurs, bien que le compilateur VC++ offre la fonction interrupteur std La version minimale qui peut être spécifiée est C++14, qui est la version par défaut à partir de Visual C++ 2013 Update 3. Vous pourriez utiliser une version plus ancienne de VC++ pour cibler C++11, mais vous devriez alors utiliser différents compilateurs VC++ pour compiler différentes unités de traduction qui ciblent différentes versions de la norme C++, ce qui casserait au minimum WPO.
AVERTISSEMENT : Ma réponse peut ne pas être complète ou très précise.