Je travaille sur une grande application logicielle qui doit fonctionner sur plusieurs plateformes. Certaines de ces plates-formes prennent en charge certaines fonctionnalités de C ++ 11 (par exemple MSVS 2010) et certaines n'en prennent pas en charge (par exemple GCC 4.3.x). Je m'attends à ce que cette situation se poursuive pendant plusieurs années (ma meilleure estimation: 3-5 ans).
Compte tenu de cela, je voudrais configurer une interface de compatibilité telle que (dans la mesure du possible) les gens puissent écrire du code C ++ 11 qui sera toujours compilé avec des compilateurs plus anciens avec un minimum de maintenance. Dans l'ensemble, l'objectif est de minimiser autant que raisonnablement possible # ifdef tout en activant la syntaxe / les fonctionnalités de base de C ++ 11 sur les plates-formes qui les prennent en charge, et de fournir une émulation sur les plates-formes qui ne le font pas.
Commençons par std :: move (). La façon la plus évidente d'atteindre la compatibilité serait de mettre quelque chose comme ça dans un fichier d'en-tête commun:
#if !defined(HAS_STD_MOVE)
namespace std { // C++11 emulation
template <typename T> inline T& move(T& v) { return v; }
template <typename T> inline const T& move(const T& v) { return v; }
}
#endif // !defined(HAS_STD_MOVE)
Cela permet aux gens d'écrire des choses comme
std::vector<Thing> x = std::move(y);
... en toute impunité. Il fait ce qu'il veut en C ++ 11 et fait de son mieux en C ++ 03. Lorsque nous supprimons enfin le dernier des compilateurs C ++ 03, ce code peut rester tel quel.
Cependant, selon la norme, il est illégal d'injecter de nouveaux symboles dans l' std
espace de noms. Voilà la théorie. Ma question est la suivante: en pratique, y a-t-il un inconvénient à le faire pour parvenir à une compatibilité aval?
Réponses:
Je travaille depuis un bon moment à maintenir un niveau de compatibilité ascendante et descendante dans mes programmes C ++, jusqu'à ce que je devais finalement en faire une boîte à outils de bibliothèque , que
je prépare pour la sortie. En général, tant que vous acceptez que vous n'obtiendrez pas de compatibilité ascendante "parfaite" ni dans les fonctionnalités (certaines choses ne peuvent tout simplement pas être émulées vers l'avant) ni dans la syntaxe (vous devrez probablement utiliser des macros, des espaces de noms alternatifs pour certaines choses) alors vous êtes prêt.De nombreuses fonctionnalités peuvent être émulées en C ++ 03 à un niveau suffisant pour une utilisation pratique - et sans tous les tracas qui accompagnent, par exemple: Boost. Heck, même la proposition de normes C ++
nullptr
suggère un backport C ++ 03. Et puis il y a TR1 par exemple pour tout ce qui est en C ++ 11, mais nous avons eu des aperçus pendant des années. Non seulement cela, certaines fonctionnalités C ++ 14 comme assert des variantes, des foncteurs transparents etoptional
peuvent être implémentées en C ++ 03!Les deux seules choses que je sais qui ne peuvent absolument pas être rétroportées sont les modèles constexpr et variadic.
En ce qui concerne toute la question de l'ajout de choses à l'espace de noms
std
, je pense que cela n'a pas d'importance - du tout. Pensez à Boost, l'une des bibliothèques C ++ les plus importantes et pertinentes, et à leur implémentation de TR1: Boost.Tr1. Si vous voulez améliorer C ++, rendez-le compatible avec C ++ 11, puis par définition, vous le transformez en quelque chose qui n'est pas C ++ 03, donc vous bloquer sur une norme que vous avez l'intention d'éviter ou de laisser derrière vous est , tout simplement, contre-productif. Les puristes se plaindront, mais par définition, il ne faut pas s'en soucier.Bien sûr, ce n'est pas parce que vous ne suivrez pas la norme (03) que vous ne pouvez pas essayer de le faire ou que vous allez joyeusement faire le tour de la briser. Ce n'est pas le propos. Tant que vous gardez un contrôle très attentif sur ce qui est ajouté à l'
std
espace de noms et que vous contrôlez les environnements dans lesquels votre logiciel est utilisé (c'est-à-dire: faites des tests!), Il ne devrait y avoir aucun dommage intraitable. Si possible, définissez tout dans un espace de noms séparé et ajoutez uniquement desusing
directives à l'espace de nomsstd
afin de ne rien y ajouter au-delà de ce qui doit "absolument" entrer. Ce qui, IINM, est plus ou moins ce que fait Boost.TR1.Mise à jour (2013) : comme la demande de la question d'origine et en voyant certains des commentaires que je ne peux pas ajouter en raison du manque de rep, voici une liste des fonctionnalités C ++ 11 et C ++ 14 et leur degré de portabilité en C ++ 03:
nullptr
: pleinement réalisable compte tenu du backport officiel du Comité; vous devrez probablement également fournir certaines spécialisations type_traits afin qu'il soit reconnu comme un type "natif".forward_list
: entièrement implémentable, bien que le support d'allocateur repose sur ce que votre implication Tr1 peut fournir.vector<int> v = {1, 2, 3, 4};
: entièrement implémentables, bien que plus verbeuses que ce que l'on souhaiterait.static_assert
: presque entièrement implémentable lorsqu'il est implémenté en tant que macro (vous n'aurez qu'à faire attention aux virgules).unique_ptr
: presque entièrement implémentable, mais vous aurez également besoin de l'aide du code appelant (pour les stocker dans des conteneurs, etc.); voir cependant ci-dessous.static_cast<>
pourrait être presque impossible.noexcept
: dépend des fonctionnalités de votre compilateur.auto
sémantique etdecltype
: dépend des caractéristiques de votre compilateur - par exemple .:__typeof__
.int16_t
, etc.): dépend des fonctionnalités de votre compilateur - ou vous pouvez déléguer à stdint.h portable.::type
modèlesconstexpr
: Non réalisable à ma connaissance.dynarray
: entièrement implémentable.optional<>
: presque entièrement implémentable tant que votre compilateur C ++ 03 prend en charge les configurations d'alignement.std::less<void>
pour le faire fonctionner.assure
): entièrement implémentables si vous voulez des assertions, presque entièrement implémentables si vous souhaitez activer les lancers à la place.(Avertissement: plusieurs de ces fonctionnalités sont implémentées dans ma bibliothèque de rétroportages C ++ que j'ai liée ci-dessus, donc je pense que je sais de quoi je parle quand je dis "complètement" ou "presque complètement".)
la source
C'est fondamentalement impossible. Considérez
std::unique_ptr<Thing>
. S'il était possible d'émuler des références rvalue en tant que bibliothèque, ce ne serait pas une fonction de langage.la source
std::unique_ptr
, mais certaines autres fonctionnalités des références rvalue ne peuvent pas être implémentées en C ++ 03, cestd::forward
n'est donc pas possible. L'autre chose est que lestd::unique_ptr
ne sera pas utile, car les collections n'utiliseront pas la sémantique de déplacement à moins que vous ne les remplaciez toutes.unique_ptr
. Regardez les défauts deauto_ptr
.unique_ptr
est pratiquement l' exemple de manuel d'une classe dont la sémantique était fondamentalement activée par la fonction de langage.unique_ptr
qui était fondamentalement activé par la fonction de langue. Il ne serait cependant pas très utile sans cette fonctionnalité. car sans transfert parfait, il ne serait pas utilisable dans de nombreux cas et un transfert parfait nécessite cette fonctionnalité.-std=c++0x
option pour les activer).std
espace de noms est un "comportement indéfini". Cela signifie que la spécification ne dit pas ce qui se passera. Mais si vous savez que sur une plate-forme particulière, la bibliothèque standard ne définit pas quelque chose, allez-y et définissez-le. Attendez-vous simplement à devoir vérifier sur chaque plateforme ce dont vous avez besoin et ce que vous pouvez définir.unique_ptr
. Cependant, cela ne serait pas trop utile, car il repose sur des collections utilisant réellement la sémantique de déplacement et celles de C ++ 03 ne le seront évidemment pas.la source