web-dev-qa-db-fra.com

Quelles différences, le cas échéant, entre C ++ 03 et C ++ 11 peuvent être détectées au moment de l'exécution?

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

Par exemple:

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

Bien sûr, 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 ressemble à ceci:

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

Je ne sais pas si les exemples sont corrects à 100%, mais vous avez l'idée. Je pense qu'il existe également d'autres façons d'écrire la même fonction.

Quelles différences, le cas échéant, entre C++ 03 et C++ 11 peuvent être détectées au moment de l'exécution? En d'autres termes, est-il possible d'écrire une fonction similaire qui retournerait une valeur booléenne indiquant si elle est compilée par un compilateur C++ 03 conforme ou un compilateur C++ 11?

bool isCpp11()
{ 
    //???
} 
116
Armen Tsirunyan

Langage de base

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 littéraux de chaîne 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 êtes susceptible d'avoir ce travail sur une véritable mise en œuvre. Celui 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 suivie 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 cas de test ne fonctionne pas pour C++ 0x dans GCC (ressemble à un bogue) et ne fonctionne pas en mode C++ 03 pour clang. n clang PR a été déposé .

Le 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();
}

Un couple de "détecter si c'est C++ 03 ou C++ 0x" peut être utilisé pour démontrer les changements de rupture. Ce qui suit est un testcase modifié, qui a été initialement utilisé pour illustrer un tel changement, mais 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 le manque 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);
}
108

Je me suis inspiré de Quels changements de rupture sont 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 nouveaux littéraux de chaîne qui ont priorité sur l'expansion des macros.

44
Karel Petranek

Que diriez-vous d'une vérification en utilisant les nouvelles règles pour >> 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;
}

Alternativement, une vérification rapide de 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));
}
33
Anthony Williams

Contrairement à C++ antérieur, 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 via, par exemple, 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); 
}

Une transmission parfaite a malheureusement pour prix de briser la compatibilité descendante.

Un autre test pourrait être basé sur les types locaux désormais autorisés comme arguments de modèle:

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);
}
16
uwedolinsky

Ce n'est pas un exemple tout à fait correct, mais c'est un exemple intéressant qui peut faire la distinction entre C et C++ 0x (il s'agit cependant de C++ 03 non valide):

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

De cette question :

struct T
{
    bool flag;
    T() : flag(false) {}
    T(const T&) : flag(true) {}
};

std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;
12
Alexandre C.

Bien qu'il ne soit pas aussi concis ... Dans le C++ actuel, le nom du modèle de classe lui-même est interprété comme un nom de type (pas un nom de modèle) dans la portée de ce modèle de classe. D'un autre côté, le nom du modèle de classe peut être utilisé comme nom de modèle en C++ 0x (N3290 14.6.1/1).

template< template< class > class > char f( int );
template< class > char (&f(...))[2];

template< class > class A {
  char i[ sizeof f< A >(0) ];
};

bool isCpp0x() {
  return sizeof( A<int> ) == 1;
}
9
Ise Wisteria
#include <utility>

template<typename T> void test(T t) { t.first = false; }

bool isCpp0x()
{
   bool b = true;
   test( std::make_pair<bool&>(b, 0) );
   return b;
}
9
Jonathan Wakely