Est-il possible pour le code C++ afin de se conformer à la fois le C ++03 standard et le C ++11 standard, mais faire des choses différentes en fonction en vertu de laquelle standard il est compilé ?
Réponses
Trop de publicités?La réponse est un oui catégorique. Sur le côté positif, il y a:
- Code qui déjà implicitement les objets copiés maintenant implicitement les déplacer lorsque cela est possible.
Sur le côté négatif, plusieurs exemples sont présentés dans l'annexe C de la norme. Même si il ya beaucoup plus de négatif que de positif, chacun d'eux est beaucoup moins susceptible de se produire.
Les littéraux de chaîne
#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"
et
#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal
Les conversions de Type 0
En C++11, uniquement des littéraux sont des entiers pointeur null constantes:
void f(void *); // #1
void f(...); // #2
template<int N> void g() {
f(0*N); // Calls #2; used to call #1
}
Arrondi des résultats après integer division et modulo
En C++03 le compilateur a été autorisé à tour vers 0 ou vers l'infini négatif. En C++11, il est obligatoire d'arrondi vers 0
int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0
Les espacements entre modèle imbriqué fermeture des accolades >> vs > >
À l'intérieur d'une spécialisation ou d'instanciation de l' >>
pourrait plutôt être interprété comme un décalage vers la droite en C++03. Ceci est plus susceptible de briser code existant: (à partir de http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/)
template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);
void total(void) {
// fon<fun<9> >(1) >> 2 in both standards
unsigned int A = fon< fun< 9 > >(1) >>(2);
// fon<fun<4> >(2) in C++03
// Compile time error in C++11
unsigned int B = fon< fun< 9 >>(1) > >(2);
}
Opérateur de new
peut maintenant lancer d'autres exceptions que l' std::bad_alloc
struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
foo *f = new foo();
catch (std::bad_alloc &) {
// c++03 code
} catch (std::exception &) {
// c++11 code
}
L'utilisateur déclaré destructeurs ont une exception implicite spécification exemple de Ce que la rupture des modifications sont introduites dans C++11?
struct A {
~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try {
A a;
} catch(...) {
// C++03 will catch the exception
}
size()
de conteneurs est maintenant nécessaire de s'exécuter en O(1)
std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03
std::ios_base::failure
ne provient pas de l' std::exception
plus
Le Code qui s'attend à découlent directement de la std::exception
peuvent se comporter différemment.
Je vous signale cet article et le suivi, ce qui a un bel exemple de la façon dont >>
peut changer de sens à partir de C++03 pour le C++11 bien encore de la compilation dans les deux.
bool const one = true;
int const two = 2;
int const three = 3;
template<int> struct fun {
typedef int two;
};
template<class T> struct fon {
static int const three = ::three;
static bool const one = ::one;
};
int main(void) {
fon< fun< 1 >>::three >::two >::one; // valid for both
}
L'élément clé est la ligne en main
, ce qui est une expression.
En C++03:
1 >> ::three = 0
=> fon< fun< 0 >::two >::one;
fun< 0 >::two = int
=> fon< int >::one
fon< int >::one = true
=> true
En C++11
fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one
::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false
Félicitations, deux résultats différents pour la même expression. Accordé, le C++03 on fait venir avec un avertissement formulaire de Bruit quand je l'ai testé.
Oui, il existe un certain nombre de changements qui vont provoquer le même code pour résultat un comportement différent entre C++03 et C++11. Les règles d'ordonnancement des différences de faire de certains des changements intéressants, dont certains déjà un comportement indéfini de devenir bien définis.
1. plusieurs mutations de la même variable dans une liste d'initialiseur
Un très intéressant coin de cas multiples mutations de la même variable à l'intérieur d'un initialiseur de la liste, par exemple:
int main()
{
int count = 0 ;
int arrInt[2] = { count++, count++ } ;
return 0 ;
}
En C++03 et C++11 ce qui est bien définie, mais l' ordre d'évaluation en C++03 est pas spécifié , mais en C++11, ils sont évaluées dans l'ordre dans lequel ils apparaissent. Donc, si nous compiler à l'aide de clang
en C++03 mode d'offrir de l'avertissement suivant (voir en direct):
warning: multiple unsequenced modifications to 'count' [-Wunsequenced]
int arrInt[2] = { count++, count++ } ;
^ ~~
mais ne pas donner l'alerte en C++11 (voir en direct).
2. De nouvelles règles d'ordonnancement faire i = ++ i + 1; bien défini en C++11
Les nouvelles règles d'ordonnancement adopté après C++03 signifie que:
int i = 0 ;
i = ++ i + 1;
n'est plus un comportement indéfini en C++11, c'est couvert dans le rapport de défaut 637. Des règles d'ordonnancement et de l'exemple de désaccord
3. De nouvelles règles d'ordonnancement également faire ++++i ; bien défini en C++11
Les nouvelles règles d'ordonnancement adopté après C++03 signifie que:
int i = 0 ;
++++i ;
n'est plus un comportement indéfini en C++11.
4. Légèrement Plus Sensible Signé À Gauche-Quarts
Les projets ultérieurs de C++11 N3485
qui je lien ci-dessous fixe le comportement indéfini de décalage d'un bit à 1 dans le passé ou le bit de signe. C'est également dans le rapport de défaut 1457. Howard Hinnant, a commenté l'importance de ce changement dans le fil sur la partie gauche de changement de vitesse (<<) un entier négatif comportement indéfini en C++11?.
5. constexpr fonctions peuvent être traités comme de la compilation des expressions constantes en C++11
C++11 introduit constexpr fonctions:
Le constexpr spécificateur déclare qu'il est possible d'évaluer la valeur de la fonction ou une variable au moment de la compilation. Ces variables et de fonctions qui peuvent ensuite être utilisés où seul moment de la compilation des expressions constantes sont autorisés.
tandis que le C++03 n'a pas le constexpr fonctionnalité que nous n'avons pas d'utiliser explicitement l' constexpr mot-clé depuis la bibliothèque standard fournit beaucoup de fonctions en C++11 constexpr. Par exemple std::numeric_limits::min. Ce qui peut conduire à des comportements différents, par exemple:
#include <limits>
int main()
{
int x[std::numeric_limits<unsigned int>::min()+2] ;
}
À l'aide de clang
en C++03 ce qui va causer x
à être d'une longueur variable tableau, qui est une extension et génère le message d'avertissement suivant:
warning: variable length arrays are a C99 feature [-Wvla-extension]
int x[std::numeric_limits<unsigned int>::min()+2] ;
^
alors qu'en C++11 std::numeric_limits<unsigned int>::min()+2
est un moment de la compilation expression constante et ne nécessite pas l'VLA extension.
6. En C++11 noexcept exception spécifications sont implicitement généré pour votre destructeurs
Depuis C++11 définis par l'utilisateur destructeur est implicite noexcept(true)
spécification comme expliqué dans noexcept destructeurs cela signifie que le programme suivant:
#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";
}
En C++11 appellera std::terminate
mais exécuté avec succès en C++03.
7. En C++03, modèle arguments ne pouvait pas avoir une liaison interne
Ce qui est couvert bien Pourquoi std::sort n'accepte pas de Comparer les classes déclarées dans une fonction. Ainsi, le code suivant ne doit pas travailler en 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;
}
}
mais actuellement, clang
permet ce code en C++03 mode avec un avertissement, sauf si vous utilisez -pedantic-errors
drapeau, ce qui est un peu dégueulasse, voir en direct.
8. >> n'est pas plus mal formé lors de la fermeture de plusieurs modèles
À l'aide de >>
à la fermeture de plusieurs modèles est pas plus mal formé, mais peut conduire à un code avec des résultats différents en C++03 et C+11. L'exemple ci-dessous est extrait à partir de l'angle Droit entre parenthèses et la rétrocompatibilité:
#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';
}
et le résultat en C++03:
0
3
et en C++11:
0
0
9. C++11 introduit quelques modifications std::vector constructeurs
Légèrement modifié le code de cette réponse montre que l'utilisation de la suite de constructeur de std::vector:
std::vector<T> test(1);
produit des résultats différents en C++03 et 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. Rétrécissement des conversions dans l'ensemble des initialiseurs
En C++11, un rétrécissement de la conversion dans l'ensemble des initialiseurs est mal formé et il ressemble gcc
permet à la fois au C++11 et C++03 bien que fournir un avertissement par défaut en C++11:
int x[] = { 2.0 };
Ce point est abordé dans le projet de C++11 section standard 8.5.4
Liste d'initialisation du paragraphe 3:
Liste d'initialisation d'un objet ou d'une référence de type T est défini comme suit:
et contient les éléments suivants balle (c'est moi qui souligne):
Sinon, si T est un type de classe, les constructeurs sont pris en compte. L'applicables constructeurs sont énumérés et le meilleur est choisi par le biais de la résolution de surcharge (13.3, 13.3.1.7). Si une conversion restrictive (voir ci-dessous) est nécessaire pour convertir le tout de l'argumentation, le programme est mal formé
Ceci et beaucoup plus d'instance, sont abordées dans le projet de norme C++ section annex C.2
C++ et ISO C++ 2003. Il comprend également:
-
De nouveaux types de littéraux de chaîne [...] plus Précisément, les macros nommé R, u8, u8R, u, uR, U, UR, ou LR ne sera pas augmenté lorsque adjacente à une chaîne littérale, mais sera interprétée comme faisant partie de la chaîne littérale. Par exemple
#define u8 "abc" const char *s = u8"def"; // Previously "abcdef", now "def"
-
Défini par l'utilisateur chaîne littérale de l'aide [...]Auparavant, n ° 1 aurait consisté à deux distincts de prétraitement des jetons et de la macro _x aurait été élargi. Dans la présente Norme Internationale, n ° 1 se compose d'un prétraitement des jetons, de sorte que la macro n'est pas développée.
#define _x "there" "hello"_x // #1
Indiquez l'arrondissement pour les résultats de l'entier / et % [...] 2003 code qui utilise la division entière arrondit le résultat vers 0 ou vers l'infini négatif, alors que ce Norme internationale toujours arrondit le résultat vers 0.
La complexité de la taille() les fonctions de membre maintenant constante [...] Certains conteneur des implémentations conformes à la C++ 2003 peut ne pas être conforme à la taille spécifiée() exigences de la présente Norme Internationale. Réglage des conteneurs tels que des std::list pour les exigences plus strictes peuvent nécessiter des modifications incompatibles.
Changement de la classe de base de std::ios_base::l'échec [...] std::ios_base::l'échec n'est plus directement dérivé de std::exception, mais est maintenant tiré de std::system_error, qui à son tour est dérivé de std::runtime_error. Valide en C++ 2003 code qui suppose que les std::ios_base::l'échec est directement dérivé de std::exception peut exécuter différemment dans la présente Norme Internationale.
Un potentiellement dangereux en arrière-incompatible changement dans les constructeurs de la séquence des conteneurs tels que std::vector
, plus précisément dans la surcharge de la spécification de la taille initiale. Où en C++03, ils ont copié par défaut-élément construit, en C++11 par défaut est de construire chacun.
Considérons cet exemple (à l'aide d' boost::shared_ptr
alors qu'il est valide en C++03):
#include <deque>
#include <iostream>
#include "boost/shared_ptr.hpp"
struct Widget
{
boost::shared_ptr<int> p;
Widget() : p(new int(42)) {}
};
int main()
{
std::deque<Widget> d(10);
for (size_t i = 0; i < d.size(); ++i)
std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n';
}
La raison en est que le C++03 spécifié d'une surcharge pour les deux "spécifier la taille et le prototype de l'élément" et de "préciser la taille seulement," comme ceci (allocateur arguments omis par souci de clarté):
container(size_type size, const value_type &prototype = value_type());
Ce sera toujours exemplaire prototype
dans le récipient size
temps. Lorsqu'il est appelé avec un seul argument, il va donc créer size
des copies d'un défaut-élément construit.
En C++11, cette signature du constructeur a été supprimé et remplacé avec ces deux surcharges:
container(size_type size);
container(size_type size, const value_type &prototype);
Le second fonctionne comme avant, la création d' size
des copies de l' prototype
élément. Toutefois, le premier (qui gère les appels avec seulement la taille de l'argument spécifié) par défaut-les constructions de chaque élément individuellement.
Je suppose que la raison de ce changement est que le C++03 surcharge de ne pas être utilisable avec un déplacer seul type d'élément. Mais c'est une modification de rupture n'en est pas moins, et rarement documentés.
Le résultat de l'échec d'une lecture à partir d'un std::istream
a changé. CppReference résume bien:
Si l'extraction échoue (par exemple, si une lettre a été saisi lorsqu'un chiffre est attendu)
value
est laissée inchangée etfailbit
est réglé. (jusqu'à ce que C++11)Si l'extraction échoue, le zéro est écrit à l'
value
etfailbit
est réglé. Si les résultats de l'extraction de la valeur trop grande ou trop petite pour tenir dansvalue
,std::numeric_limits<T>::max()
oustd::numeric_limits<T>::min()
est écrit etfailbit
indicateur est défini. (depuis C++11)
C'est principalement un problème si vous êtes habitué à la nouvelle sémantique et d'avoir à écrire à l'aide de C++03. La suite n'est pas particulièrement bien pratique, mais bien défini en C++11:
int x, y;
std::cin >> x >> y;
std::cout << x + y;
Toutefois, en C++03, le code ci-dessus utilise une variable non initialisée et a donc un comportement indéterminé.