Est-il possible que le code C ++ soit conforme à la fois à la norme C ++ 03 et à la norme C ++ 11 , mais fait des choses différentes selon la norme sous laquelle il est compilé?
c++
c++11
language-lawyer
c++03
Erik Sjölund
la source
la source
auto
pourrait entraîner une situation comme celle-ci>>
lorsqu'il est utilisé dans un modèle. Vous pouvez trouver une situation où il peut compiler pour les deux normes. Un autre qui, j'en suis sûr, serait facile à trouver est celui de l'initialisation.auto
puisse provoquer cela. Avec l'ancienne signification, uneauto
déclaration nécessite un nom de type; avec la nouvelle signification, un nom de type n'est pas autorisé.Réponses:
La réponse est définitivement affirmative. Du côté positif, il y a:
Côté négatif, plusieurs exemples sont répertoriés dans l'annexe C de la norme. Même s'il y en a beaucoup plus de négatifs que de positifs, chacun d'eux est beaucoup moins susceptible de se produire.
Littéraux de chaîne
et
Type de conversions de 0
En C ++ 11, seuls les littéraux sont des constantes de pointeur nul entier:
Résultats arrondis après division entière et modulo
En C ++ 03, le compilateur était autorisé à arrondir vers 0 ou vers l'infini négatif. En C ++ 11, il est obligatoire d'arrondir vers 0
Espaces entre les accolades fermantes du modèle imbriqué >> vs>>
À l'intérieur d'une spécialisation ou d'une instanciation, le
>>
pourrait plutôt être interprété comme un décalage à droite en C ++ 03. Cependant, cela est plus susceptible de casser le code existant: (à partir de http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )L'opérateur
new
peut désormais lever d'autres exceptions questd::bad_alloc
Les destructeurs déclarés par l'utilisateur ont un exemple de spécification d'exception implicite tiré de Quelles modifications de rupture sont introduites dans C ++ 11?
size()
des conteneurs est désormais requis pour fonctionner en O (1)std::ios_base::failure
ne dérive pas directement destd::exception
plusBien que la classe de base directe soit nouvelle, elle
std::runtime_error
ne l'est pas. Donc:la source
noexecpt(true)
doncthrow
dans un destructeur va maintenant appelerstd::terminate
. Mais j'espère que quiconque a écrit un tel code en sera heureux!catch (std::exception &)
il attrape toujoursstd::ios_base::failure
.operator new
est précis (il peut maintenant être lancéstd::bad_array_new_length
), mais votre exemple ne le montre pas du tout. Le code que vous montrez est le même en C ++ 03 et C ++ 11 AFAIK.Je vous renvoie à cet article et à la suite , qui a un bel exemple de la façon de
>>
changer le sens de C ++ 03 à C ++ 11 tout en compilant les deux.L'élément clé est la ligne
main
, qui est une expression.En C ++ 03:
En C ++ 11
Félicitations, deux résultats différents pour la même expression. Certes, le C ++ 03 a fourni un formulaire d'avertissement Clang lorsque je l'ai testé.
la source
typename
pour::two
en C ++ 03 Versiontrue
oufalse
pour les différentes normes. Nous pourrions peut-être l'utiliser comme test de fonctionnalité </joke>warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
), mais toujours un bel exemple de la façon dont l'::
opérateur ambigu change le sens (en se référant à la portée globale ou en déréférençant celui qui se trouve juste devant lui)Oui, il existe un certain nombre de modifications qui entraîneront le même code à entraîner un comportement différent entre C ++ 03 et C ++ 11. Les différences de règles de séquençage entraînent des changements intéressants, dont certains comportements auparavant non définis devenant bien définis.
1. plusieurs mutations de la même variable dans une liste d'initialisation
Un cas d'angle très intéressant serait de multiples mutations de la même variable dans une liste d'initialisation, par exemple:
En C ++ 03 et C ++ 11, cela est bien défini mais l' ordre d'évaluation en C ++ 03 n'est pas spécifié mais en C ++ 11, ils sont évalués dans l'ordre dans lequel ils apparaissent . Donc, si nous compilons en utilisant
clang
en mode C ++ 03, il fournit l'avertissement suivant ( voir en direct ):mais ne fournit pas d'avertissement en C ++ 11 ( voir en direct ).
2. De nouvelles règles de séquencement font i = ++ i + 1; bien défini en C ++ 11
Les nouvelles règles de séquençage adoptées après C ++ 03 signifient que:
n'est plus un comportement indéfini en C ++ 11, cela est traité dans le rapport de défauts 637. Règles de séquencement et exemple en désaccord
3. De nouvelles règles de séquençage font également ++++ i; bien défini en C ++ 11
Les nouvelles règles de séquençage adoptées après C ++ 03 signifient que:
n'est plus un comportement indéfini en C ++ 11.
4. Décalages à gauche légèrement plus sensibles et signés
Les versions ultérieures de C ++ 11 incluent
N3485
que je lie ci-dessous fixe le comportement indéfini de décaler un bit dans ou après le bit de signe . Ceci est également couvert dans le rapport de défauts 1457 . Howard Hinnant a commenté la signification de ce changement dans le fil de discussion sur Est-ce que le décalage vers la gauche (<<) est un comportement négatif entier non défini en C ++ 11? .5. Les fonctions constexpr peuvent être traitées comme des expressions de constante de temps de compilation en C ++ 11
C ++ 11 a introduit des fonctions constexpr qui:
tandis que C ++ 03 n'a pas la fonctionnalité constexpr , nous n'avons pas à utiliser explicitement le mot clé constexpr car la bibliothèque standard fournit de nombreuses fonctions en C ++ 11 comme constexpr . Par exemple std :: numeric_limits :: min . Ce qui peut conduire à des comportements différents, par exemple:
L'utilisation
clang
en C ++ 03 entraînerax
un tableau de longueur variable, qui est une extension et générera l'avertissement suivant:en C ++ 11
std::numeric_limits<unsigned int>::min()+2
est une expression de constante de temps de compilation et ne nécessite pas l'extension VLA.6. En C ++ 11, aucune spécification d'exception n'est générée implicitement pour vos destructeurs
Étant donné qu'en C ++ 11, le destructeur défini par l'utilisateur a une
noexcept(true)
spécification implicite comme expliqué dans noexcept destructors, cela signifie que le programme suivant:En C ++ 11 appellera
std::terminate
mais s'exécutera avec succès en C ++ 03.7. En C ++ 03, les arguments de modèle ne pouvaient pas avoir de liaison interne
Ceci est bien décrit dans Pourquoi std :: sort n'accepte pas les classes Compare déclarées dans une fonction . Le code suivant ne devrait donc pas fonctionner en C ++ 03:
mais
clang
autorise actuellement ce code en mode C ++ 03 avec un avertissement, sauf si vous utilisez un-pedantic-errors
indicateur, ce qui est un peu icky, voyez-le en direct .8. >> n'est plus mal formé lors de la fermeture de plusieurs modèles
Utiliser
>>
pour fermer plusieurs modèles n'est plus mal formé mais peut conduire à du code avec des résultats différents en C ++ 03 et C + 11. L'exemple ci-dessous est tiré des supports à angle droit et de la compatibilité descendante :et le résultat en C ++ 03 est:
et en C ++ 11:
9. C ++ 11 change certains des constructeurs std :: vector
Le code légèrement modifié de cette réponse montre que l'utilisation du constructeur suivant de std :: vector :
produit des résultats différents en C ++ 03 et C ++ 11:
10. Rétrécissement des conversions dans les initialiseurs globaux
En C ++ 11, une conversion de rétrécissement dans les initialiseurs d'agrégation est mal formée et il semble que
gcc
cela soit possible en C ++ 11 et C ++ 03 bien qu'elle fournisse un avertissement par défaut en C ++ 11:Ceci est traité dans le projet de section standard d'
8.5.4
initialisation de la liste C ++ 11 paragraphe 3 :et contient la puce suivante (c'est moi qui souligne ):
Cette instance et bien d'autres sont traitées dans le projet de section standard
annex C.2
C ++ C ++ et ISO C ++ 2003 . Il comprend également:Nouveaux types de littéraux de chaîne [...] Plus précisément, les macros nommées R, u8, u8R, u, uR, U, UR ou LR ne seront pas développées lorsqu'elles sont adjacentes à un littéral de chaîne mais seront interprétées comme faisant partie du littéral de chaîne . Par exemple
Prise en charge des chaînes littérales définies par l'utilisateur [...] Auparavant, le n ° 1 aurait consisté en deux jetons de prétraitement distincts et la macro _x aurait été étendue. Dans la présente Norme internationale, # 1 consiste en un seul jeton de prétraitement, de sorte que la macro n'est pas développée.
Spécifiez l'arrondi pour les résultats du code entier / et% [...] 2003 qui utilise la division entière arrondit le résultat vers 0 ou vers l'infini négatif, tandis que la présente Norme internationale arrondit toujours le résultat vers 0.
La complexité des fonctions membres size () est désormais constante [...] Certaines implémentations de conteneurs conformes à C ++ 2003 peuvent ne pas être conformes aux exigences size () spécifiées dans la présente Norme internationale. L'ajustement de conteneurs tels que std :: list aux exigences plus strictes peut nécessiter des modifications incompatibles.
Modifier la classe de base de std :: ios_base :: failure [...] std :: ios_base :: failure n'est plus dérivé directement de std :: exception, mais est désormais dérivé de std :: system_error, qui à son tour est dérivé de std :: runtime_error. Un code C ++ 2003 valide qui suppose que std :: ios_base :: failure est dérivé directement de std :: exception peut s'exécuter différemment dans la présente Norme internationale.
la source
Un changement potentiellement dangereux en arrière incompatible est dans les constructeurs de conteneurs de séquence tels que
std::vector
, spécifiquement dans la surcharge spécifiant la taille initiale. Là où en C ++ 03, ils ont copié un élément construit par défaut, en C ++ 11 ils ont construit chacun par défaut.Considérez cet exemple (en utilisant
boost::shared_ptr
pour qu'il soit valide C ++ 03):Exemple C ++ 03 Live
Exemple C ++ 11 Live
La raison en est que C ++ 03 a spécifié une surcharge pour "spécifier la taille et l'élément prototype" et "spécifier la taille uniquement", comme ceci (arguments d'allocateur omis pour des raisons de concision):
Cela sera toujours copié
prototype
dans lessize
temps du conteneur . Lorsqu'il est appelé avec un seul argument, il crée donc dessize
copies d'un élément construit par défaut.En C ++ 11, cette signature de constructeur a été supprimée et remplacée par ces deux surcharges:
Le second fonctionne comme auparavant, créant des
size
copies duprototype
élément. Cependant, le premier (qui gère désormais les appels avec uniquement l'argument taille spécifié) construit par défaut chaque élément individuellement.Je suppose que la raison de ce changement est que la surcharge C ++ 03 ne serait pas utilisable avec un type d'élément à déplacement uniquement. Mais c'est un changement de rupture néanmoins, et rarement documenté à ce sujet.
la source
deque
détention de dix widgets distincts, et non pas dix widgets partageant la même ressource.Le résultat d'un échec de lecture à partir d'un
std::istream
a changé. CppReference le résume bien:C'est principalement un problème si vous êtes habitué à la nouvelle sémantique et que vous devez ensuite écrire en C ++ 03. Ce qui suit n'est pas particulièrement une bonne pratique mais bien défini en C ++ 11:
Cependant, en C ++ 03, le code ci-dessus utilise une variable non initialisée et a donc un comportement non défini.
la source
int x = 1, y = 1; cin >> x >> y; cout << x*y;
. Avec C ++ 03, cela aurait produit correctementx
quand aucun ney
pouvait être lu.Ce thread Quelles différences, le cas échéant, entre C ++ 03 et C ++ 0x peuvent être détectées au moment de l'exécution ont des exemples (copiés à partir de ce thread) pour déterminer les différences de langage, par exemple en exploitant l'effondrement des références C ++ 11:
et c ++ 11 autorisant les types locaux comme paramètres de modèle:
la source
Voici un autre exemple:
Tirages:
Voir le résultat sur Coliru
la source