สามารถตรวจพบความแตกต่างอะไรระหว่าง C ++ 03 และ C ++ 11 ในขณะรันไทม์


116

เป็นไปได้ที่จะเขียนฟังก์ชันซึ่งเมื่อคอมไพล์ด้วยคอมไพเลอร์ C จะส่งกลับ 0 และเมื่อคอมไพล์ด้วยคอมไพเลอร์ C ++ จะส่งคืน 1 (การเจือจางเล็กน้อยด้วย #ifdef __cplusplusไม่น่าสนใจ)

ตัวอย่างเช่น:

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

แน่นอนข้างต้นจะใช้ได้ก็ต่อเมื่อsizeof (char)ไม่เหมือนกับsizeof (int)

อีกวิธีหนึ่งที่พกพาสะดวกกว่าคือสิ่งนี้:

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

ฉันไม่แน่ใจว่าตัวอย่างถูกต้อง 100% แต่คุณเข้าใจแล้ว ฉันเชื่อว่ามีวิธีอื่นในการเขียนฟังก์ชันเดียวกันด้วย

สามารถตรวจพบความแตกต่างอะไรระหว่าง C ++ 03 และ C ++ 11 ในขณะรันไทม์ กล่าวอีกนัยหนึ่งเป็นไปได้หรือไม่ที่จะเขียนฟังก์ชันที่คล้ายกันซึ่งจะส่งคืนค่าบูลีนที่ระบุว่าคอมไพเลอร์โดยคอมไพเลอร์ C ++ 03 ที่สอดคล้องหรือคอมไพเลอร์ C ++ 11

bool isCpp11()
{ 
    //???
} 

10
และประเด็นของการออกกำลังกายนี้คืออะไร? ประการแรกคุณมีมาโครและประการที่สองจะใช้เวลาหลายปีก่อนที่คอมไพเลอร์จะเริ่มใช้คุณสมบัติทั้งหมดของ C ++ 0x ในขณะเดียวกันก็จะเป็นการผสมผสาน ดังนั้นการทดสอบที่สมเหตุสมผลเพียงอย่างเดียวคือคอมไพเลอร์มาโครเวอร์ชัน
Gene Bushuyev

4
สิ่งนี้เหมาะกับการเรียกเก็บเงินที่ไม่ใช่คำถามจริง แต่ดูเหมือนว่าน่าสนใจเกินไปที่จะปฏิบัติตามกฎ!
David Heffernan

4
@Gene et al: คุณลงคะแนนคำถามทั้งหมดที่น่าสนใจ แต่คุณไม่เห็น "ประเด็น" เชิงปฏิบัติหรือไม่
Armen Tsirunyan

2
"เราคาดหวังว่าคำตอบโดยทั่วไปจะเกี่ยวข้องกับข้อเท็จจริงการอ้างอิงหรือความเชี่ยวชาญเฉพาะด้าน" ฉันคิดว่าคำถามนี้ตรงตามความคาดหวังเหล่านี้โหวตให้เปิดอีกครั้ง
Karel Petranek

6
@sixlettervariables: แม้ว่าจะเปิดกว้างสำหรับการโต้แย้งอย่างแน่นอนว่าการใช้วลีอาจจะดีกว่า แต่สำหรับฉันแล้วฉันคิดว่าแนวคิดพื้นฐานของคำถาม (ความแตกต่างใดระหว่าง C ++ 03 และ C ++ 0x ที่สามารถตรวจพบได้เมื่อทำงาน - เวลา?) ถูกต้องตามกฎหมายอย่างสมบูรณ์ เนื่องจากโค้ดต้องคอมไพล์และดำเนินการทั้งสองอย่างจึงสามารถใช้วลีได้ว่าเกี่ยวข้องกับการเปลี่ยนแปลงที่ทำลายใน C ++ 0x ดูเหมือนว่าฉันจะเป็นคำถามที่ถูกต้องตามกฎหมายที่จะถามเช่นกัน
Jerry Coffin

คำตอบ:


108

ภาษาหลัก

การเข้าถึงตัวแจงนับโดยใช้:::

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

คุณยังสามารถใช้คำหลักใหม่ในทางที่ผิด

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

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

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

นอกจากนี้ความจริงที่ว่าตัวอักษรสตริงไม่ได้แปลงเป็นไฟล์ char*

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

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

ฉันไม่รู้ว่าคุณมีแนวโน้มที่จะใช้งานได้จริงแค่ไหน หนึ่งที่หาประโยชน์auto

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

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

ต่อไปนี้ขึ้นอยู่กับข้อเท็จจริงที่operator int&&เป็นฟังก์ชันการแปลงเป็นint&&ใน C ++ 0x และการแปลงintตามด้วยตรรกะและใน 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);
}

กรณีทดสอบนั้นใช้ไม่ได้กับ C ++ 0x ใน GCC (ดูเหมือนบั๊ก) และไม่ทำงานในโหมด C ++ 03 สำหรับเสียงดัง ประชาสัมพันธ์เสียงดังกราวได้รับการยื่น

การแก้ไขการปรับเปลี่ยนชื่อคลาสของเทมเพลตใน 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();
}

สามารถใช้ "ตรวจสอบว่านี่คือ C ++ 03 หรือ C ++ 0x" สองสามรายการเพื่อแสดงการเปลี่ยนแปลงที่ไม่สมบูรณ์ ต่อไปนี้เป็นกรณีทดสอบที่ปรับแต่งซึ่งในตอนแรกใช้เพื่อแสดงการเปลี่ยนแปลงดังกล่าว แต่ตอนนี้ใช้เพื่อทดสอบ C ++ 0x หรือ 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); }

ห้องสมุดมาตรฐาน

การตรวจจับการขาดoperator void*ใน 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
ดี ฉันสามารถยืนยันได้ว่าโซลูชันนี้ใช้ได้กับ g ++ (GCC) 4.6.0 ทั้งที่มีและไม่มี -std = c ++ 0x
Alexander

2
สิ่งนี้ส่งคืนtrueสำหรับ MSVC 2005 เป็นต้นไปและข้อผิดพลาดในการคอมไพล์ใน MSVC 2003
Anthony Williams

1
โอ้ที่รักพวกเขาทำลายความเข้ากันได้แบบย้อนกลับ!
avakar

14
@ โจฮันเนส: นี่เป็นความสนุกที่สุดที่คุณมีในรอบหลายสัปดาห์ใช่ไหม ; -]
ildjarn

4
ฉันคิดว่าสิ่งเหล่านี้น่าสนใจมาก แต่ฉันคิดว่าสิ่งที่ฉลาดที่สุดคือการโทร(...)VS (char*)ฉันชอบมันมาก!
corsiKa

44

ฉันได้รับแรงบันดาลใจจากการเปลี่ยนแปลงที่สำคัญอะไรบ้างใน C ++ 11 :

#define u8 "abc"

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

สิ่งนี้ยึดตามตัวอักษรสตริงใหม่ที่มีความสำคัญเหนือการขยายมาโคร


1
+1: น่าสนใจมาก แต่ในทางเทคนิคแล้วข้อกำหนดในการไม่ใช้ตัวประมวลผลล่วงหน้า แต่ข้อ จำกัด ไม่ได้ตั้งใจที่จะยกเลิกคำตอบที่ดีเช่นนี้ :)
Armen Tsirunyan

1
ถ้าคุณทำตามฟังก์ชันด้วย#undef u8การใช้งานตัวประมวลผลล่วงหน้าจะสังเกตได้ก็ต่อเมื่อโปรแกรมของคุณมีมาโครที่กำหนดไว้ก่อนหน้านี้ชื่อu8(boooo) หากนั่นเป็นข้อกังวลที่แท้จริงก็ยังสามารถแก้ไขได้โดยใช้ pragmas / การเรียกมาโครแบบพุช / ป๊อปเฉพาะการใช้งาน (ฉันเชื่อว่าการใช้งานส่วนใหญ่มีสิ่งเหล่านี้)
James McNellis

3
ข้อโต้แย้งที่สมเหตุสมผลอย่างหนึ่งคือในระบบ C ++ 03 อาจมีคน #define u8 เพื่อจัดเตรียมความสามารถ C ++ 0x จำลอง ยังฉันชอบคำตอบจริงๆ
Christopher Smith

1
คุณสามารถย้ายฟังก์ชัน isCpp0x นี้ไปยังหน่วยการแปลที่แยกจากกันได้ดังนั้นสิ่งนี้มาโครจะไม่ส่งผลต่อโค้ดอื่น ๆ
unkulunkulu

1
ฉันคิดว่ามีความแตกต่างระหว่างการใช้ตัวประมวลผลล่วงหน้าเพื่อพึ่งพาคอมไพลเลอร์ที่ตั้งค่ามาโครบางอย่างและการใช้ตัวประมวลผลล่วงหน้าเพื่อตรวจจับคุณสมบัติภาษาจริง นั่นเป็นเหตุผลที่ฉันไม่คิดว่าคำตอบนี้โกง
Lightness Races in Orbit

33

วิธีการตรวจสอบโดยใช้กฎใหม่สำหรับการ>>ปิดเทมเพลต:

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

หรือตรวจสอบอย่างรวดเร็วสำหรับ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 เป็นแนวคิดที่ยอดเยี่ยมจริงๆ :) อย่างไรก็ตามในทางปฏิบัติสิ่งนี้จะทำลายด้วย Visual C ++ 2005/2088 ซึ่งไม่รองรับ C ++ 0x แต่อนุญาตให้ใช้ >> ในเทมเพลตด้วยวิธี C ++ 0x
Karel Petranek

4
โอ้; ฉันชอบการละเมิด ADL! อย่างไรก็ตามการใช้งาน C ++ 03 ที่สอดคล้องกันไม่สามารถตั้งชื่อฟังก์ชันได้std::moveหรือไม่?
James McNellis

1
@FredOverflow: ฉันจะไม่รำคาญ UI ห่วย!
Lightness Races ใน Orbit

16

ซึ่งแตกต่างจาก C ++ ก่อนหน้านี้ C ++ 0x อนุญาตให้สร้างชนิดการอ้างอิงจากประเภทการอ้างอิงหากประเภทการอ้างอิงพื้นฐานนั้นถูกนำมาใช้ตัวอย่างเช่นพารามิเตอร์เทมเพลต:

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

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

การส่งต่อที่สมบูรณ์แบบมาพร้อมกับการทำลายความเข้ากันได้แบบย้อนกลับไปอย่างน่าเสียดาย

การทดสอบอื่นอาจใช้ประเภทโลคัลที่อนุญาตในขณะนี้เป็นอาร์กิวเมนต์เทมเพลต:

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

1
+1 เป็นความคิดที่ดีฉันไม่แน่ใจว่าisC++0xเป็นตัวระบุ C ++ ที่ถูกต้องหรือไม่)
Karel Petranek

1
การอ้างอิงที่ดีสำหรับการอ้างอิงจากการอนุมานการอ้างอิงคืออะไร?
Kerrek SB

@ kerrek-sb: ร่างพูดถึงเรื่องนี้ใน 8.3.2.6 (อ้างอิง)
uwedolinsky

15

นี่ไม่ใช่ตัวอย่างที่ถูกต้อง แต่เป็นตัวอย่างที่น่าสนใจที่สามารถแยกแยะ C กับ C ++ 0x ได้ (แม้ว่า C ++ 03 จะไม่ถูกต้อง):

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

10
ในทางเทคนิคนี้อาศัยsizeof(int) != 1ความเป็นจริง ในระบบ 0x ที่มีขนาดใหญ่charเป็นพิเศษผลลัพธ์อาจเหมือนกัน ยังคงเป็นเคล็ดลับที่เรียบร้อยแม้ว่า
Dennis Zickefoose

@ เดนนิส - charเป็นหนึ่งไบต์เสมอ
โหนด

4
@ โหนด: หนึ่งไบต์ไม่ใช่ 8 บิตเสมอไป
Alexandre C.

2
@ โหนดsizeof(char)จะเป็น 1 เสมอตามนิยาม แต่CHAR_BIT(กำหนดไว้ใน limit.h) ได้รับอนุญาตให้มีค่ามากกว่า 8 ด้วยเหตุนี้ทั้งสองcharและintอาจมี 32 บิตซึ่งในกรณีนี้sizeof(int) == 1(และCHAR_BIT == 32)
Sjoerd

12

จากคำถามนี้ :

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

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

ฉันสงสัยว่ามันทำงานได้อย่างไร เมื่อลองแล้วตอนนี้ชัดเจนแล้ว: มีข้อบกพร่องเล็กน้อย จะได้ผลถ้าคุณเปลี่ยนบรรทัดสุดท้ายเป็น:bool is_cpp0x = !test[0].flag;
awx

1
เป็นไปได้: โครงสร้างเริ่มต้น C ++ 0x Tในขณะที่ C ++ 03 คัดลอกสร้างจากT()
awx

9

แม้ว่าจะไม่รัดกุมนัก ... ใน C ++ ปัจจุบันชื่อเทมเพลตคลาสเองจะถูกตีความว่าเป็นชื่อชนิด (ไม่ใช่ชื่อเทมเพลต) ในขอบเขตของเทมเพลตคลาสนั้น ในทางกลับกันชื่อเทมเพลตคลาสสามารถใช้เป็นชื่อเทมเพลตใน 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 ในทางเทคนิคจะทดสอบไลบรารีมาตรฐานไม่ใช่คอมไพเลอร์และแม้ว่าจะเป็น C ++ 03 ที่ถูกต้องและ C ++ 0x ที่ถูกต้อง แต่ก็ไม่ใช่ C ++ 98 ที่ถูกต้องดังนั้นด้วยการปรับแต่งบางอย่างเพื่อตรวจจับ C ++ 98 / C ++ 03 / C ++ 0x stdlib
Jonathan Wakely
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.