Welche Unterschiede zwischen C ++ 03 und C ++ 11 können gegebenenfalls zur Laufzeit festgestellt werden?

116

Es ist möglich, eine Funktion zu schreiben, die beim Kompilieren mit einem C-Compiler 0 und beim Kompilieren mit einem C ++ - Compiler 1 zurückgibt (die triviale Lösung mit #ifdef __cplusplusist nicht interessant).

Beispielsweise:

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

Natürlich funktioniert das oben Genannte nur, wenn sizeof (char)es nicht dasselbe ist wiesizeof (int)

Eine andere, tragbarere Lösung ist ungefähr so:

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

Ich bin nicht sicher, ob die Beispiele 100% korrekt sind, aber Sie bekommen die Idee. Ich glaube, es gibt auch andere Möglichkeiten, dieselbe Funktion zu schreiben.

Welche Unterschiede zwischen C ++ 03 und C ++ 11 können gegebenenfalls zur Laufzeit festgestellt werden? Mit anderen Worten, ist es möglich, eine ähnliche Funktion zu schreiben, die einen booleschen Wert zurückgibt, der angibt, ob sie von einem konformen C ++ 03-Compiler oder einem C ++ 11-Compiler kompiliert wird?

bool isCpp11()
{ 
    //???
} 
Armen Tsirunyan
quelle
10
Und wozu dient diese Übung? Erstens haben Sie ein Makro, und zweitens wird es einige Jahre dauern, bis Compiler mit der Implementierung aller Funktionen von C ++ 0x beginnen. In der Zwischenzeit wird es eine Mischung sein. Der einzig vernünftige Test ist also, dass der Compiler ein Versionsmakro erstellt.
Gene Bushuyev
4
Dies passt nicht zu einer echten Frage, aber es scheint zu interessant, die Regeln zu befolgen!
David Heffernan
4
@Gene et al.: Stimmen Sie alle Fragen ab, die interessant sind, aber Sie sehen den pragmatischen "Punkt" nicht?
Armen Tsirunyan
2
"Wir erwarten, dass Antworten im Allgemeinen Fakten, Referenzen oder spezifisches Fachwissen beinhalten." Ich denke, diese Frage erfüllt diese Erwartungen, stimmen Sie für die Wiedereröffnung.
Karel Petranek
6
@sixlettervariables: Es ist zwar durchaus zu argumentieren, dass die Formulierung besser sein könnte, aber es scheint mir, dass der Grundbegriff der Frage (welche Unterschiede, wenn überhaupt, zwischen C ++ 03 und C ++ 0x, bei run- festgestellt werden können) Zeit?) ist absolut legitim. Angesichts der Tatsache, dass der Code in beiden kompiliert und ausgeführt werden muss, könnte er auch so formuliert werden, dass es sich um die wichtigsten Änderungen in C ++ 0x handelt. Das scheint mir auch eine absolut legitime Frage zu sein.
Jerry Coffin

Antworten:

108

Kernsprache

Zugriff auf einen Enumerator mit :::

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

Sie können die neuen Schlüsselwörter auch missbrauchen

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

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

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

Auch die Tatsache, dass String-Literale nicht mehr konvertiert werden char*

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

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

Ich weiß jedoch nicht, wie wahrscheinlich es ist, dass Sie an einer echten Implementierung arbeiten. Eine, die ausnutztauto

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

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

Das Folgende basiert auf der Tatsache, dass operator int&&es sich um eine Konvertierungsfunktion int&&in C ++ 0x und eine Konvertierung in intgefolgt von logisch und in C ++ 03 handelt

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

Dieser Testfall funktioniert nicht für C ++ 0x in GCC (sieht aus wie ein Fehler) und funktioniert nicht im C ++ 03-Modus für Clang. Eine klirrende PR wurde eingereicht .

Die modifizierte Behandlung injizierter Klassennamen von Vorlagen 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();
}

Ein paar "Erkennen, ob dies C ++ 03 oder C ++ 0x ist" können verwendet werden, um wichtige Änderungen zu demonstrieren. Das Folgende ist ein optimierter Testfall, der ursprünglich verwendet wurde, um eine solche Änderung zu demonstrieren, jetzt aber zum Testen auf C ++ 0x oder C ++ 03 verwendet wird.

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

Standardbibliothek

Erkennen des Mangels an 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);
}
Johannes Schaub - litb
quelle
1
Nett. Ich kann bestätigen, dass diese Lösung hier mit g ++ (GCC) 4.6.0 funktioniert, sowohl mit als auch ohne -std = c ++ 0x.
Alexander
2
Dies kehrt truefür MSVC 2005 und einen Kompilierungsfehler in MSVC 2003 zurück.
Anthony Williams
1
Oh je, sie haben die Abwärtskompatibilität gebrochen!
Avakar
14
@Johannes: Das ist der größte Spaß, den du seit Wochen hattest, nicht wahr? ; -]
ildjarn
4
Ich finde das alles sehr interessant, aber ich denke, das klügste sind die (...)vs- (char*)Anrufe. Das gefällt mir sehr!
CorsiKa
44

Ich habe mich inspirieren lassen von Welche bahnbrechenden Änderungen werden in C ++ 11 eingeführt? ::

#define u8 "abc"

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

Dies basiert auf den neuen Zeichenfolgenliteralen, die Vorrang vor der Makroerweiterung haben.

Karel Petranek
quelle
1
+1: Sehr interessant, bricht aber technisch die Anforderung, den Präprozessor nicht zu verwenden. Aber die Einschränkung sollte nicht so nette Antworten
verwerfen
1
Wenn Sie der Funktion mit a folgen, kann #undef u8die Präprozessorverwendung nur beobachtet werden, wenn Ihr Programm über ein zuvor definiertes Makro mit dem Namen u8(boooo) verfügt. Wenn dies ein echtes Problem ist, kann dies immer noch mit implementierungsspezifischen Push / Pop-Makro-Pragmas / -Aufrufen umgangen werden (die meisten Implementierungen haben diese, glaube ich).
James McNellis
3
Ein ziemlich vernünftiges Argument ist, dass auf einem C ++ 03-System möglicherweise jemand #8 definiert, um simulierte C ++ 0x-Funktionen bereitzustellen. Trotzdem gefällt mir die Antwort sehr gut.
Christopher Smith
1
Sie können diese isCpp0x-Funktion einfach in eine separate Übersetzungseinheit verschieben, damit diese Makrosache keinen Einfluss auf anderen Code hat.
Unkulunkulu
1
Ich denke, es gibt einen Unterschied zwischen der Verwendung des Präprozessors, um sich darauf zu verlassen, dass der Compiler einen Makrowert einstellt, und der Verwendung des Präprozessors, um tatsächliche Sprachmerkmale zu erkennen. Deshalb glaube ich nicht, dass diese Antwort betrügt.
Leichtigkeitsrennen im Orbit
33

Wie wäre es mit einer Prüfung unter Verwendung der neuen Regeln zum >>Schließen von Vorlagen:

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

Alternativ eine schnelle Überprüfung auf 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));
}
Anthony Williams
quelle
6
+1 Eine coole Idee :) In der Praxis wird dies jedoch mit Visual C ++ 2005/2088 brechen, das C ++ 0x nicht unterstützt, aber die Verwendung von >> in Vorlagen auf C ++ 0x-Weise ermöglicht.
Karel Petranek
4
Oooh; Ich mag den ADL-Missbrauch! Konnte eine konforme C ++ 03-Implementierung jedoch keine Funktion mit dem Namen haben std::move?
James McNellis
1
@FredOverflow: Ich würde mich nicht darum kümmern. Die Benutzeroberfläche ist scheiße!
Leichtigkeitsrennen im Orbit
16

Im Gegensatz zu früheren Versionen von C ++ können mit C ++ 0x Referenztypen aus Referenztypen erstellt werden, wenn dieser Basisreferenztyp beispielsweise über einen Vorlagenparameter eingeführt wird:

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

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

Eine perfekte Weiterleitung kostet leider die Abwärtskompatibilität.

Ein weiterer Test könnte auf jetzt zulässigen lokalen Typen als Vorlagenargumente basieren:

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);
}
uwedolinsky
quelle
Vielleicht hätte ich daraus eine Merkmalsklasse machen sollen;)
uwedolinsky
1
+1 Gute Idee, ich bin mir nur nicht sicher, ob isC++0xes sich um eine gültige C ++ - Kennung handelt;)
Karel Petranek
1
Was ist eine gute Referenz für die Referenz aus der Referenzinferenz?
Kerrek SB
@ Kerrek-sb: Der Entwurf spricht darüber in 8.3.2.6 (Referenzen)
uwedolinsky
15

Dies ist kein richtiges Beispiel, aber es ist ein interessantes Beispiel, das C von C ++ 0x unterscheiden kann (es ist jedoch ungültiges C ++ 03):

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}
Adam Rosenfield
quelle
10
Technisch gesehen hängt dies davon ab, sizeof(int) != 1dass es wahr ist. Auf einem 0x-System mit außergewöhnlich großen chars können die Ergebnisse gleich sein. Trotzdem ein ordentlicher Trick.
Dennis Zickefoose
@ Tennis - charist immer ein Byte
Knoten
4
@Node: Ein Byte besteht nicht immer aus 8 Bits.
Alexandre C.
2
@Node sizeof(char)ist per Definition immer 1. Aber CHAR_BIT(definiert in limits.h) darf mehr als 8 sein. Infolgedessen können beide charund int32 Bits haben, in welchem ​​Fall sizeof(int) == 1(und CHAR_BIT == 32).
Sjoerd
12

Aus dieser Frage :

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

std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;
Alexandre C.
quelle
Ich fragte mich, wie das funktionieren könnte. Nachdem ich es ausprobiert habe, ist jetzt klar: Es gibt einen kleinen Fehler. es funktioniert, wenn Sie die letzte Zeile ändern in:bool is_cpp0x = !test[0].flag;
awx
1
plausibel: C ++ 0x Standardkonstrukte, Twährend C ++ 03 Kopierkonstrukte vonT()
awx
9

Obwohl nicht so prägnant ... In aktuellem C ++ wird der Name der Klassenvorlage selbst als Typname (kein Vorlagenname) im Bereich dieser Klassenvorlage interpretiert. Andererseits kann der Name der Klassenvorlage in C ++ 0x (N3290 14.6.1 / 1) als Vorlagenname verwendet werden.

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;
}
Ise Glyzinien
quelle
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;
}
Jonathan Wakely
quelle
NB technisch testet dies die Standardbibliothek, nicht den Compiler, und obwohl es C ++ 03 und C ++ 0x gültig ist, ist es C ++ 98 nicht gültig, so dass mit einigen Optimierungen vorgenommen werden könnte, um ein C ++ 98 / zu erkennen C ++ 03 / C ++ 0x stdlib
Jonathan Wakely