Compilateurs C++ plus stricts (compilateurs C++ améliorés par Clang)
Les compilateurs C++ basés sur Clang sont basés sur Clang. Les compilateurs C++ basés sur Clang prennent en charge C++11, et sont aussi des compilateur plus stricts que BCC32.
Sommaire
Syntaxe structurelle
BCC32 autorise la combinaison de __try
avec catch
.
Les compilateurs C++ basés sur Clang exigent try/catch
(standard C++) ou __try/__except/__finally
(extensions Microsoft).
Conversions de types
L'assignation de chaînes C littérales en char *
génère un avertissement sauf si ce pointeur est déclaré comme const
.
BCC32 autorise l'assignation entre char *
et unsigned char*
sans aucun avertissement, mais dans les compilateurs C++ basés sur Clang, cela est considéré comme une simple erreur de non concordance de type.
La conversion implicite de int
en (classique, non de type sécurisé) enum
est autorisée dans BCC32 et ne génère pas d'avertissement. Dans les compilateurs C++ basés sur Clang, la conversion implicite n'est pas autorisée, et requiert un transtypage. Cela se produit couramment avec les énumérations comme TColor
et TCursor
:
TColor stones = (TColor) 0; // instead of defaulting to random color, paint it black
- Remarque : Vcl.Graphics.hpp définit TColor sur clBlack.
Définition de membres statiques
Les membres de données statiques C++ doivent être définis à un seul emplacement dans votre application. Vous devez les déclarer dans un en-tête de votre déclaration de classe, puis définir le membre de données dans un fichier source (pas dans un fichier d'en-tête).
Par exemple :
foo.h:
struct X { static int static_member; };
foo.cpp:
#include "foo.h"
int X::me;
bar.cpp:
#include "foo.h"
int some_function() { return X::me; }
Dans cet exemple, foo.cpp comporte la définition de X::me, et bar.cpp montre une utilisation. Le membre statique est déclaré dans foo.h, mais il est seulement défini dans foo.cpp.
Nos outils 32 bits autorisaient des définitions multiples de X::me, en violation avec le standard C++. Vous pouviez donc effectuer ceci :
foo.h:
struct X { static int static_member; };
int X::me;
Dans le cas ci-dessus (erroné pour les compilateurs C++ basés sur Clang, mais autorisé par BCC32), vous définissez le membre de données statique dans l'en-tête, ce qui entraînera de multiples définitions de X::me, une pour chaque emplacement dans lequel l'en-tête est inclus. Nos outils basés sur Clang n'autorisent pas cela, et vous pouvez obtenir des erreurs du lieur telles que :
[ilink64 Error] Error: Public symbol 'triplet::sum' defined in both module C:\USERS\WIN764\NODES.O and C:\USERS\WIN764\UNIT1.O
Si ces erreurs de définition de symboles dupliqués vous sont renvoyées par le lieur pour les membres de données statiques, vous devez rechercher le modèle erroné ci-dessus et corriger votre code pour qu'il soit conforme à l'exigence "une seule définition" concernant les membres de données statiques.
Recherche de noms en deux phases dans les templates
La résolution de noms dans les templates s'effectue en deux temps : la définition et l'instanciation. Des compilateurs moins conformes ont tendance à différer la résolution jusqu'à l'instanciation et permettent parfois à du code qui n'est pas strictement conforme au standard de se compiler avec succès. BCC32 ne compile pas de templates à moins qu'ils ne soient instanciés. Les définitions de template contenant des erreurs évidentes seront compilées si elles ne sont jamais utilisées. Les compilateurs C++ basés sur Clang signalent ces erreurs.
La recherche de noms en deux phases a des effets subtils, comme le fait de ne pas vérifier les classes de base tel que cela est expliqué dans le blog "LLVM Project". Pour ce problème spécifique, la solution consiste à qualifier les identificateurs à trouver dans les classes de base avec this->
.
Les types dépendants qualifiés requièrent le mot clé typename
Voici ce que fait ce template :
template <class T>
void pointerOrMultiply() {
T::member *var;
}
Le but pourrait être de créer un pointeur sur un membre du type de template, mais avec certains paramètres de template, cela pourrait aussi être une multiplication, ce qui annulerait le résultat. Pour éviter toute ambiguïté ne pouvant être résolue tant que le template n'a pas été instancié, le standard requiert que les types qualifiés qui sont dépendants du paramètre de template doivent être précédés du mot clé typename
, même si cette interprétation est la seule qui aurait un sens lors de l'instanciation :
template <class T>
void definitelyAPointer() {
typename T::member *var;
}
Fusion de tokens
Les compilateurs C++ basés sur Clang appliquent la règle qui oblige que le résultat d'une opération de fusion de tokens du préprocesseur (avec l'opérateur ##
) soit un seul token valide.
Voici un exemple arrangé : Supposons que la base de code définit une macro de type fonction qui utilise conditionnellement un nom tel quel ou le qualifie dans un espace de nommage :
#ifdef KEEP_IT_SIMPLE
#define qual(C,M) M
#else
#define qual(C,M) C##::##M
#endif
// ...
void notRecommendedJustAnExample(int counter) {
qual(std, cout) << counter;
}
BCC32 compilera le dernier cas ; il fonctionnera comme prévu. Mais en fait, ce code n'est pas officiellement supporté et les compilateurs C++ basés sur Clang vous en avertiront :
pasting formed 'std::', an invalid preprocessing token pasting formed '::cout', an invalid preprocessing token
Deux instances de ##
produisent deux erreurs. (Remarque : l'ordre de tokens multiples pour les opérations de fusion dans une seule invocation de macro n'est pas défini.) Aucune des deux instances ne résulte en un seul token ; elle tentent toutes deux de combiner un identificateur et un ponctuateur (l'opérateur de résolution de portée).
La solution dans ce genre de situation est simple : il n'est pas vraiment nécessaire de fusionner les tokens. L'identificateur et l'opérateur restent adjacents de toute façon. La fusion est uniquement requise pour créer un nouveau token (ce qui est sujet à d'autres développements de macro). La macro pourrait donc être définie comme ceci :
#define qual(C,M) C::M