Quali differenze, se presenti, tra C ++ 03 e C ++ 11 possono essere rilevate in fase di esecuzione?


116

È possibile scrivere una funzione, che, quando compilata con un compilatore C, restituirà 0, e quando compilata con un compilatore C ++, restituirà 1 (la banale soluzione con #ifdef __cplusplusnon è interessante).

Per esempio:

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

Ovviamente, quanto sopra funzionerà solo se sizeof (char)non è lo stesso disizeof (int)

Un'altra soluzione più portatile è qualcosa del genere:

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

Non sono sicuro che gli esempi siano corretti al 100%, ma hai un'idea. Credo che ci siano anche altri modi per scrivere la stessa funzione.

Quali differenze, se presenti, tra C ++ 03 e C ++ 11 possono essere rilevate in fase di esecuzione? In altre parole, è possibile scrivere una funzione simile che restituisca un valore booleano che indica se è compilata da un compilatore C ++ 03 conforme o da un compilatore C ++ 11?

bool isCpp11()
{ 
    //???
} 

10
E qual è il punto di questo esercizio? In primo luogo, hai una macro e, in secondo luogo, ci vorranno alcuni anni prima che i compilatori inizino a implementare tutte le funzionalità di C ++ 0x, nel frattempo sarà un mix. Quindi l'unico test ragionevole è che il compilatore sia una macro di versione.
Gene Bushuyev

4
Questo si adatta al conto di non una vera domanda, ma sembra troppo interessante seguire le regole!
David Heffernan

4
@Gene et al: Hai sottovalutato tutte le domande che sono interessanti ma non vedi il "punto" pragmatico?
Armen Tsirunyan

2
"Ci aspettiamo che le risposte generalmente coinvolgano fatti, riferimenti o competenze specifiche". Penso che questa domanda risponda a queste aspettative, voto per la riapertura.
Karel Petranek

6
@sixlettervariables: anche se è certamente aperto all'argomento che il fraseggio potrebbe essere migliore, mi sembra che la nozione fondamentale della domanda (quali differenze, se ce ne sono, tra C ++ 03 e C ++ 0x possono essere rilevate in fase di esecuzione- tempo?) è perfettamente legittimo. Dato che il codice deve essere compilato ed eseguito in entrambi, potrebbe anche essere formulato come riguardante le modifiche sostanziali in C ++ 0x. Anche questa mi sembra una domanda perfettamente legittima da porre.
Jerry Coffin

Risposte:


108

Lingua principale

Accesso a un enumeratore utilizzando :::

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

Puoi anche abusare delle nuove parole chiave

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

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

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

Inoltre, il fatto che i valori letterali stringa non vengano più convertiti in char*

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

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

Non so quanto sia probabile che questo funzioni su un'implementazione reale. Uno che sfruttaauto

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

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

Quanto segue si basa sul fatto che operator int&&è una funzione di conversione int&&in C ++ 0x e una conversione in intseguita da logica e in 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);
}

Quel test case non funziona per C ++ 0x in GCC (sembra un bug) e non funziona in modalità C ++ 03 per clang. È stato presentato un clang PR .

Il trattamento modificato dei nomi di classe iniettati dei modelli in 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 paio di "rileva se questo è C ++ 03 o C ++ 0x" possono essere utilizzati per dimostrare le modifiche più importanti. Quello che segue è un testcase ottimizzato, che inizialmente è stato utilizzato per dimostrare tale modifica, ma ora viene utilizzato per testare C ++ 0x o 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); }

Libreria standard

Rilevamento della mancanza di operator void*in 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);
}

1
Bello. Posso confermare che questa soluzione funziona qui con g ++ (GCC) 4.6.0, sia con che senza -std = c ++ 0x.
Alexander

2
Ciò ritorna trueper MSVC 2005 in poi e un errore di compilazione in MSVC 2003.
Anthony Williams

1
Oh caro, hanno rotto la retrocompatibilità!
avakar

14
@Johannes: questa è la cosa più divertente che hai avuto da settimane, non è vero? ; -]
ildjarn

4
Li trovo molto interessanti, ma penso che il più intelligente sia il (...)vs (char*)call. Mi piace molto!
corsiKa

44

Ho tratto ispirazione da Quali modifiche sostanziali vengono introdotte in C ++ 11? :

#define u8 "abc"

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

Si basa sui nuovi valori letterali di stringa che hanno la precedenza sull'espansione della macro.


1
+1: Molto interessante, in effetti, ma tecnicamente rompe il requisito di non utilizzare il preprocessore. Ma la restrizione non aveva lo scopo di respingere risposte così belle :)
Armen Tsirunyan

1
Bene, se segui la funzione con un, #undef u8l'utilizzo del preprocessore è osservabile solo se il tuo programma ha una macro definita in precedenza denominatau8 (boooo). Se questa è una vera preoccupazione, può ancora essere aggirata usando pragmi / chiamate macro push / pop specifici per l'implementazione (la maggior parte delle implementazioni li ha, credo).
James McNellis

3
Un argomento abbastanza ragionevole è che su un sistema C ++ 03 qualcuno potrebbe #definire u8 per fornire funzionalità C ++ 0x simulate. Tuttavia, mi piace molto la risposta.
Christopher Smith,

1
puoi semplicemente spostare questa funzione isCpp0x in un'unità di traduzione separata in modo che questa macro non influenzi altro codice.
unkulunkulu

1
Penso che ci sia una differenza tra l'utilizzo del preprocessore per fare affidamento sul compilatore che imposta un valore macro e l'utilizzo del preprocessore per rilevare le caratteristiche del linguaggio effettivo. Ecco perché non penso che questa risposta stia tradendo.
Gare di leggerezza in orbita

33

Che ne dici di un controllo utilizzando le nuove regole per la >>chiusura dei modelli:

#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;
}

In alternativa un rapido controllo per 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));
}

6
+1 Un'idea davvero interessante :) Tuttavia, in pratica questo romperà con Visual C ++ 2005/2088 che non supporta C ++ 0x ma consente di utilizzare >> nei modelli in modo C ++ 0x.
Karel Petranek

4
Oooh; Mi piace l'abuso di ADL! Tuttavia, un'implementazione C ++ 03 conforme non potrebbe avere una funzione denominata std::move?
James McNellis

1
@FredOverflow: non mi preoccuperei. L'interfaccia utente fa schifo!
Gare di leggerezza in orbita il

16

A differenza del C ++ precedente, C ++ 0x consente la creazione di tipi di riferimento da tipi di riferimento se tale tipo di riferimento di base viene introdotto, ad esempio, tramite un parametro di modello:

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

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

Sfortunatamente, l'inoltro perfetto ha il prezzo di rompere la compatibilità con le versioni precedenti.

Un altro test potrebbe essere basato sui tipi locali ora consentiti come argomenti del modello:

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

Forse avrei dovuto trasformarlo in una classe di tratti;)
uwedolinsky

1
+1 Bella idea, solo non sono sicuro che isC++0xsia un identificatore C ++ valido;)
Karel Petranek

1
Qual è un buon riferimento per il riferimento dall'inferenza dei riferimenti?
Kerrek SB

@ kerrek-sb: La bozza ne parla in 8.3.2.6 (Riferimenti)
uwedolinsky

15

Questo non è un esempio corretto, ma è un esempio interessante che può distinguere C e C ++ 0x (tuttavia non è C ++ 03 non valido):

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

10
Tecnicamente, questo si basa sizeof(int) != 1sull'essere vero. Su un sistema 0x con messaggi eccezionalmente grandi char, i risultati potrebbero essere gli stessi. Comunque un bel trucco.
Dennis Zickefoose

@Dennis - charè sempre un byte però
Nodo

4
@Node: un byte non è sempre di 8 bit.
Alexandre C.

2
@Node sizeof(char)sarà sempre 1, per definizione. Ma CHAR_BIT(definito in limits.h) può essere maggiore di 8. Di conseguenza, entrambi chare intpotrebbero avere 32 bit, nel qual caso sizeof(int) == 1(e CHAR_BIT == 32).
Sjoerd

12

Da questa domanda :

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

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

Mi chiedevo come potesse funzionare; dopo averlo provato, ora è chiaro: c'è un piccolo bug. funziona se si modifica l'ultima riga in:bool is_cpp0x = !test[0].flag;
awx

1
plausibile: costrutti predefiniti C ++ 0x Tmentre C ++ 03 copia i costrutti daT()
awx

9

Sebbene non sia così conciso ... Nell'attuale C ++, il nome del modello di classe stesso viene interpretato come un nome di tipo (non un nome di modello) nell'ambito di quel modello di classe. D'altra parte, il nome del modello di classe può essere utilizzato come nome del modello in 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
#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;
}

NB tecnicamente questo verifica la libreria standard non il compilatore, e sebbene sia valido C ++ 03 e valido C ++ 0x, non è valido C ++ 98, quindi con alcune modifiche potrebbe essere fatto per rilevare un C ++ 98 / C ++ 03 / C ++ 0x stdlib
Jonathan Wakely
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.