Perbedaan apa, jika ada, antara C ++ 03 dan C ++ 11 yang dapat dideteksi saat run-time?

116

Dimungkinkan untuk menulis sebuah fungsi, yang, ketika dikompilasi dengan kompilator C akan mengembalikan 0, dan ketika dikompilasi dengan kompilator C ++, akan mengembalikan 1 (sulusi sepele dengan #ifdef __cplusplustidak menarik).

Sebagai contoh:

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

Tentu saja, hal di atas hanya akan berfungsi jika sizeof (char)tidak sama dengansizeof (int)

Solusi lain yang lebih portabel adalah seperti ini:

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

Saya tidak yakin apakah contohnya 100% benar, tetapi Anda mengerti. Saya yakin ada cara lain untuk menulis fungsi yang sama juga.

Perbedaan apa, jika ada, antara C ++ 03 dan C ++ 11 yang dapat dideteksi saat run-time? Dengan kata lain, apakah mungkin untuk menulis fungsi serupa yang akan mengembalikan nilai boolean yang menunjukkan apakah itu dikompilasi oleh kompilator C ++ 03 yang sesuai atau kompiler C ++ 11?

bool isCpp11()
{ 
    //???
} 
Armen Tsirunyan
sumber
10
Dan apa gunanya latihan ini? Pertama, Anda memiliki makro, dan kedua akan membutuhkan beberapa tahun sebelum kompiler mulai mengimplementasikan semua fitur C ++ 0x, sementara itu akan menjadi campuran. Jadi satu-satunya pengujian yang masuk akal adalah compiler makro versi.
Gene Bushuyev
4
Ini sesuai dengan tagihan bukan pertanyaan nyata tetapi tampaknya terlalu menarik untuk mengikuti aturan!
David Heffernan
4
@ Gen dkk: Apakah Anda tidak menyukai semua pertanyaan yang menarik tetapi Anda tidak melihat "poin" yang pragmatis?
Armen Tsirunyan
2
"Kami mengharapkan jawaban secara umum melibatkan fakta, referensi, atau keahlian khusus". Saya pikir pertanyaan ini memenuhi harapan ini, pilih untuk dibuka kembali.
Karel Petranek
6
@sixlettervariables: meskipun jelas terbuka untuk argumen bahwa ungkapan bisa lebih baik, menurut saya gagasan mendasar dari pertanyaan (apa perbedaan, jika ada, antara C ++ 03 dan C ++ 0x yang dapat dideteksi saat menjalankan- waktu?) sangat sah. Mengingat bahwa kode harus dikompilasi dan dijalankan di keduanya, itu juga bisa diutarakan sebagai tentang perubahan yang melanggar di C ++ 0x. Bagi saya itu juga seperti pertanyaan yang sangat sah untuk ditanyakan.
Jerry Coffin

Jawaban:

108

Bahasa Inti

Mengakses enumerator menggunakan :::

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

Anda juga dapat menyalahgunakan kata kunci baru

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

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

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

Juga, fakta bahwa string literal tidak lagi diubah menjadi char*

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

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

Saya tidak tahu seberapa besar kemungkinan Anda memiliki ini bekerja pada implementasi nyata. Salah satu yang mengeksploitasiauto

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

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

Berikut ini didasarkan pada fakta bahwa operator int&&ada fungsi konversi ke int&&dalam C ++ 0x, dan konversi ke intdiikuti oleh logika-dan di 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);
}

Kasus uji tersebut tidak berfungsi untuk C ++ 0x di GCC (terlihat seperti bug) dan tidak berfungsi dalam mode C ++ 03 untuk clang. Sebuah dentang PR telah diajukan .

The pengobatan modifikasi dari nama kelas disuntikkan template di 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();
}

Sepasang "deteksi apakah ini C ++ 03 atau C ++ 0x" dapat digunakan untuk mendemonstrasikan perubahan yang melanggar. Berikut ini adalah kasus pengujian yang diubah, yang awalnya digunakan untuk mendemonstrasikan perubahan tersebut, tetapi sekarang digunakan untuk menguji C ++ 0x atau 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); }

Perpustakaan Standar

Mendeteksi kekurangan operator void*dalam 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
sumber
1
Bagus. Saya dapat mengonfirmasi bahwa solusi ini berfungsi di sini dengan g ++ (GCC) 4.6.0, baik dengan maupun tanpa -std = c ++ 0x.
Alexander
2
Ini mengembalikan trueuntuk MSVC 2005 dan seterusnya, dan kesalahan kompilasi di MSVC 2003.
Anthony Williams
1
Ya ampun, mereka merusak kompatibilitas mundur!
avakar
14
@Johannes: Ini adalah yang paling menyenangkan yang pernah Anda alami dalam beberapa minggu, bukan? ; -]
ildjarn
4
Saya menemukan ini semua sangat menarik, tapi saya pikir yang paling pintar adalah panggilan (...)vs. (char*)Saya sangat suka itu!
corsiKa
44

Saya mendapat inspirasi dari Perubahan apa saja yang diperkenalkan di C ++ 11? :

#define u8 "abc"

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

Ini didasarkan pada string literal baru yang lebih diutamakan daripada perluasan makro.

Karel Petranek
sumber
1
+1: Memang sangat menarik, tetapi secara teknis melanggar persyaratan untuk tidak menggunakan preprocessor. Tetapi pembatasan tersebut tidak dimaksudkan untuk mengabaikan jawaban yang begitu bagus :)
Armen Tsirunyan
1
Nah, jika Anda mengikuti fungsi dengan a #undef u8maka penggunaan preprocessor hanya dapat diamati jika program Anda memiliki makro yang ditentukan sebelumnya bernama u8(boooo). Jika itu adalah masalah nyata, itu masih dapat diatasi dengan menggunakan pragma / panggilan makro push / pop khusus implementasi (saya yakin sebagian besar implementasi memiliki ini).
James McNellis
3
Satu argumen yang cukup masuk akal adalah bahwa pada sistem C ++ 03 seseorang mungkin #define u8 untuk menyediakan kapabilitas C ++ 0x yang disimulasikan. Tetap saja, saya sangat menyukai jawabannya.
Christopher Smith
1
Anda bisa memindahkan fungsi isCpp0x ini ke unit terjemahan terpisah sehingga makro ini tidak akan memengaruhi kode lain.
unkulunkulu
1
Saya rasa ada perbedaan antara menggunakan preprocessor untuk mengandalkan compiler yang mengatur beberapa nilai makro, dan menggunakan preprocessor untuk mendeteksi fitur bahasa yang sebenarnya. Itu sebabnya saya tidak berpikir bahwa jawaban ini curang.
Balapan Ringan di Orbit
33

Bagaimana dengan cek menggunakan aturan baru untuk >>menutup template:

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

Atau pemeriksaan cepat untuk 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
sumber
6
+1 Benar-benar ide yang keren :) Namun, dalam praktiknya ini akan rusak dengan Visual C ++ 2005/2088 yang tidak mendukung C ++ 0x tetapi memungkinkan >> untuk digunakan dalam template dengan cara C ++ 0x.
Karel Petranek
4
Oooh; Saya suka penyalahgunaan ADL! Namun, tidak bisakah implementasi C ++ 03 yang sesuai memiliki fungsi bernama std::move?
James McNellis
1
@FredOverflow: Saya tidak akan repot. UI payah!
Balapan Ringan di Orbit
16

Tidak seperti C ++ sebelumnya, C ++ 0x memungkinkan jenis referensi dibuat dari jenis referensi jika jenis referensi dasar tersebut dimasukkan melalui, misalnya, parameter template:

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

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

Sayangnya, penerusan sempurna datang dengan harga merusak kompatibilitas ke belakang.

Pengujian lain dapat didasarkan pada tipe lokal yang sekarang diizinkan sebagai argumen template:

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
sumber
Mungkin saya seharusnya mengubahnya menjadi kelas sifat;)
uwedolinsky
1
+1 Ide bagus, saya hanya tidak yakin apakah isC++0xpengenal C ++ yang valid;)
Karel Petranek
1
Apa referensi yang baik untuk referensi dari referensi inferensi?
Kerrek SB
@ kerrek-sb: Draf berbicara tentang ini di 8.3.2.6 (Referensi)
uwedolinsky
15

Ini bukan contoh yang benar, tetapi ini adalah contoh menarik yang dapat membedakan C vs. C ++ 0x (meskipun C ++ 03 tidak valid):

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}
Adam Rosenfield
sumber
10
Secara teknis, ini bergantung pada sizeof(int) != 1kebenaran. Pada sistem 0x dengan chars yang sangat besar , hasilnya bisa sama. Masih trik yang rapi.
Dennis Zickefoose
@ Dennis - charselalu satu byte
Node
4
@Node: satu byte tidak selalu 8 bit.
Alexandre C.
2
@Node sizeof(char)akan selalu 1, menurut definisi. Tapi CHAR_BIT(didefinisikan dalam limit.h) diperbolehkan lebih dari 8. Akibatnya, keduanya chardan intbisa memiliki 32 bit, dalam hal ini sizeof(int) == 1(dan CHAR_BIT == 32).
Sjoerd
12

Dari pertanyaan ini :

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.
sumber
Saya bertanya-tanya bagaimana ini bisa berhasil; setelah mencobanya, sekarang jelas: ada sedikit bug. ini berfungsi jika Anda mengubah baris terakhir ke:bool is_cpp0x = !test[0].flag;
awx
1
masuk akal: C ++ 0x konstruksi default Tsedangkan C ++ 03 menyalin konstruksi dariT()
awx
9

Meskipun tidak begitu ringkas ... Dalam C ++ saat ini, nama template kelas itu sendiri diartikan sebagai nama jenis (bukan nama template) dalam cakupan template kelas tersebut. Di sisi lain, nama template kelas dapat digunakan sebagai nama template di 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;
}
Ise Wisteria
sumber
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
sumber
NB secara teknis ini menguji pustaka standar bukan kompilernya, dan meskipun C ++ 03 valid dan C ++ 0x valid, ini bukan C ++ 98 yang valid, jadi dengan beberapa penyesuaian dapat dilakukan untuk mendeteksi C ++ 98 / C ++ 03 / C ++ 0x stdlib
Jonathan Wakely