116 votes

Quelles sont les différences éventuelles entre C++03 et C++11 qui peuvent être détectées lors de l'exécution ?

Il est possible d'écrire une fonction qui, lorsqu'elle est compilée avec un compilateur C, renvoie 0, et qui, lorsqu'elle est compilée avec un compilateur C++, renvoie 1 (la solution triviale avec #ifdef __cplusplus n'est pas intéressant).

Par exemple :

int isCPP()
{
    return sizeof(char) == sizeof 'c';
}

Bien entendu, ce qui précède ne fonctionnera que si sizeof (char) n'est pas la même chose que sizeof (int)

Une autre solution, plus portable, est la suivante :

int isCPP()
{
    typedef int T;
    {
       struct T 
       {
           int a[2];
       };
       return sizeof(T) == sizeof(struct T);
    }
}

Je ne suis pas sûr que les exemples soient corrects à 100 %, mais vous voyez ce que je veux dire. Je pense qu'il existe également d'autres façons d'écrire la même fonction.

Quelles différences, s'il y en a, entre C++03 et C++11 peuvent être détectées lors de l'exécution ? En d'autres termes, est-il possible d'écrire une fonction similaire qui renverrait une valeur booléenne indiquant si elle est compilée par un compilateur C++03 conforme ou par un compilateur C++11 ?

bool isCpp11()
{ 
    //???
}

108voto

Johannes Schaub - litb Points 256113

Langue principale

Accès à un énumérateur à l'aide de :: :

template<int> struct int_ { };

template<typename T> bool isCpp0xImpl(int_<T::X>*) { return true; }
template<typename T> bool isCpp0xImpl(...) { return false; }

enum A { X };
bool isCpp0x() {
  return isCpp0xImpl<A>(0);
}

Vous pouvez également abuser des nouveaux mots-clés

struct a { };
struct b { a a1, a2; };

struct c : a {
  static b constexpr (a());
};

bool isCpp0x() {
  return (sizeof c::a()) == sizeof(b);
}

De plus, le fait que les chaînes de caractères ne se convertissent plus en char*

bool isCpp0xImpl(...) { return true; }
bool isCpp0xImpl(char*) { return false; }

bool isCpp0x() { return isCpp0xImpl(""); }

Je ne sais pas si vous avez des chances que cela fonctionne dans le cadre d'une mise en œuvre réelle. Une implémentation qui exploite auto

struct x { x(int z = 0):z(z) { } int z; } y(1);

bool isCpp0x() {
  auto x(y);
  return (y.z == 1);
}

Ce qui suit est basé sur le fait que operator int&& est une fonction de conversion en int&& en C++0x, et une conversion en int suivi de logique-et en C++03

struct Y { bool x1, x2; };

struct A {
  operator int();
  template<typename T> operator T();
  bool operator+();
} a;

Y operator+(bool, A);

bool isCpp0x() {
  return sizeof(&A::operator int&& +a) == sizeof(Y);
}

Ce test-case ne fonctionne pas pour C++0x dans GCC (on dirait un bug) et ne fonctionne pas en mode C++03 pour clang. Un PR clang a été déposé .

En traitement modifié des noms de classe injectés des modèles en C++11 :

template<typename T>
bool g(long) { return false; }

template<template<typename> class>
bool g(int) { return true; }

template<typename T>
struct A {
  static bool doIt() {
    return g<A>(0);
  }
};

bool isCpp0x() {
  return A<void>::doIt();
}

Quelques "détections de C++03 ou de C++0x" peuvent être utilisées pour démontrer les ruptures. Ce qui suit est un scénario de test modifié, qui a d'abord été utilisé pour démontrer un tel changement, mais qui est maintenant utilisé pour tester C++0x ou C++03.

struct X { };
struct Y { X x1, x2; };

struct A { static X B(int); };
typedef A B;

struct C : A {
  using ::B::B; // (inheriting constructor in c++0x)
  static Y B(...);
};

bool isCpp0x() { return (sizeof C::B(0)) == sizeof(Y); }

Bibliothèque standard

Détecter l'absence de operator void* en C++0x std::basic_ios

struct E { E(std::ostream &) { } };

template<typename T>
bool isCpp0xImpl(E, T) { return true; }
bool isCpp0xImpl(void*, int) { return false; }

bool isCpp0x() {
  return isCpp0xImpl(std::cout, 0);
}

44voto

Karel Petranek Points 10740

Je me suis inspiré de Quels sont les changements radicaux introduits dans C++11 ? :

#define u8 "abc"

bool isCpp0x() {
   const std::string s = u8"def"; // Previously "abcdef", now "def"
   return s == "def";
}

Ceci est basé sur les nouvelles chaînes de caractères qui ont la priorité sur l'expansion des macros.

33voto

Anthony Williams Points 28904

Que diriez-vous d'une vérification selon les nouvelles règles pour >> les modèles de fermeture :

#include <iostream>

const unsigned reallyIsCpp0x=1;
const unsigned isNotCpp0x=0;

template<unsigned>
struct isCpp0xImpl2
{
    typedef unsigned isNotCpp0x;
};

template<typename>
struct isCpp0xImpl
{
    static unsigned const reallyIsCpp0x=0x8000;
    static unsigned const isNotCpp0x=0;
};

bool isCpp0x() {
    unsigned const dummy=0x8000;
    return isCpp0xImpl<isCpp0xImpl2<dummy>>::reallyIsCpp0x > ::isNotCpp0x>::isNotCpp0x;
}

int main()
{
    std::cout<<isCpp0x()<<std::endl;
}

Une autre solution consiste à vérifier rapidement la présence des éléments suivants std::move :

struct any
{
    template<typename T>
    any(T const&)
    {}
};

int move(any)
{
    return 42;
}

bool is_int(int const&)
{
    return true;
}

bool is_int(any)
{
    return false;
}

bool isCpp0x() {
    std::vector<int> v;
    return !is_int(move(v));
}

16voto

uwedolinsky Points 381

Contrairement au C++ antérieur, le C++0x permet de créer des types de référence à partir de types de référence si ce type de référence de base est introduit par le biais, par exemple, d'un paramètre de modèle :

template <class T> bool func(T&) {return true; } 
template <class T> bool func(...){return false;} 

bool isCpp0x() 
{
    int v = 1;
    return func<int&>(v); 
}

Le transfert parfait se fait au prix d'une rupture de la compatibilité ascendante, malheureusement.

Un autre test pourrait être basé sur les types locaux désormais autorisés en tant qu'arguments de modèles :

template <class T> bool cpp0X(T)  {return true;} //cannot be called with local types in C++03
                   bool cpp0X(...){return false;}

bool isCpp0x() 
{
   struct local {} var;
   return cpp0X(var);
}

15voto

Adam Rosenfield Points 176408

Cet exemple n'est pas tout à fait correct, mais c'est un exemple intéressant qui permet de distinguer le C du C++0x (il n'est cependant pas valable pour le C++03) :

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X