Sự khác biệt nào, nếu có, giữa C ++ 03 và C ++ 11 có thể được phát hiện trong thời gian chạy?


116

Có thể viết một hàm, khi được biên dịch bằng trình biên dịch C sẽ trả về 0, và khi được biên dịch bằng trình biên dịch C ++, sẽ trả về 1 (điều #ifdef __cplusplusnày không thú vị chút nào).

Ví dụ:

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

Tất nhiên, những điều trên sẽ chỉ hoạt động nếu sizeof (char)không giống nhưsizeof (int)

Một giải pháp khác, linh hoạt hơn là một cái gì đó như thế này:

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

Tôi không chắc liệu các ví dụ có đúng 100% hay không, nhưng bạn có thể hiểu được. Tôi tin rằng có nhiều cách khác để viết cùng một hàm.

Sự khác biệt nào, nếu có, giữa C ++ 03 và C ++ 11 có thể được phát hiện trong thời gian chạy? Nói cách khác, có thể viết một hàm tương tự sẽ trả về một giá trị boolean cho biết nó được biên dịch bởi trình biên dịch C ++ 03 phù hợp hay trình biên dịch C ++ 11 không?

bool isCpp11()
{ 
    //???
} 

10
Và điểm của bài tập này là gì? Thứ nhất, bạn có một macro, và thứ hai sẽ mất một vài năm trước khi các trình biên dịch bắt đầu triển khai tất cả các tính năng của C ++ 0x, trong khi đó nó sẽ là một hỗn hợp. Vì vậy, thử nghiệm hợp lý duy nhất là trình biên dịch một macro phiên bản.
Gene Bushuyev

4
Điều này phù hợp với dự luật không phải là một câu hỏi thực sự nhưng nó có vẻ quá thú vị để tuân theo các quy tắc!
David Heffernan

4
@Gene và cộng sự: Bạn có phản đối tất cả các câu hỏi thú vị nhưng bạn không thấy "điểm" thực dụng?
Armen Tsirunyan

2
"Chúng tôi mong đợi câu trả lời thường liên quan đến sự kiện, tài liệu tham khảo hoặc kiến ​​thức chuyên môn cụ thể". Tôi nghĩ câu hỏi này đáp ứng những mong đợi này, hãy bỏ phiếu để mở lại.
Karel Petranek

6
@sixlettervariables: trong khi chắc chắn có ý kiến ​​tranh luận rằng cách viết có thể tốt hơn, nhưng đối với tôi, có vẻ như khái niệm cơ bản của câu hỏi (những khác biệt nào, nếu có, giữa C ++ 03 và C ++ 0x có thể được phát hiện khi chạy- thời gian?) là hoàn toàn hợp pháp. Cho rằng mã phải biên dịch và thực thi trong cả hai, nó cũng có thể được diễn giải như về những thay đổi vi phạm trong C ++ 0x. Đối với tôi, đó dường như là một câu hỏi hoàn toàn chính đáng để hỏi.
Jerry Coffin

Câu trả lời:


108

Ngôn ngữ chính

Tiếp cận điều tra viên bằng :::

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

Bạn cũng có thể lạm dụng các từ khóa mới

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

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

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

Ngoài ra, thực tế là các ký tự chuỗi không còn chuyển đổi thành char*

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

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

Tuy nhiên, tôi không biết bạn có khả năng làm điều này như thế nào trên một triển khai thực sự hay không. Một khai thácauto

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

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

Điều sau dựa trên thực tế operator int&&là một hàm chuyển đổi thành int&&trong C ++ 0x và một chuyển đổi thành inttheo sau bởi logic-và trong 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);
}

Trường hợp thử nghiệm đó không hoạt động đối với C ++ 0x trong GCC (có vẻ như lỗi) và không hoạt động ở chế độ C ++ 03 đối với tiếng kêu. Một PR rõ ràng đã được đệ trình .

Các điều trị sửa đổi của tên lớp tiêm mẫu trong 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();
}

Một vài "phát hiện xem đây là C ++ 03 hay C ++ 0x" có thể được sử dụng để chứng minh các thay đổi đột phá. Sau đây là một testcase đã được tinh chỉnh, ban đầu được sử dụng để chứng minh sự thay đổi như vậy, nhưng bây giờ được sử dụng để kiểm tra C ++ 0x hoặc 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); }

Thư viện tiêu chuẩn

Phát hiện thiếu operator void*trong 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
Đẹp. Tôi có thể xác nhận rằng giải pháp này hoạt động ở đây với g ++ (GCC) 4.6.0, cả có và không có -std = c ++ 0x.
Alexander

2
Đây lợi nhuận truecho MSVC 2005 trở đi, và một lỗi biên dịch trong MSVC 2003.
Anthony Williams

1
Ôi trời, họ đã phá vỡ khả năng tương thích ngược!
avakar

14
@Johannes: Đây là niềm vui nhất mà bạn đã có trong nhiều tuần, phải không? ; -]
ildjarn

4
Tôi thấy những điều này rất thú vị, nhưng tôi nghĩ thông minh nhất là các cuộc gọi (...)vs. (char*)Tôi thực sự thích điều đó!
corsiKa

44

Tôi lấy cảm hứng từ Những thay đổi đột phá nào được giới thiệu trong C ++ 11? :

#define u8 "abc"

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

Điều này dựa trên các ký tự chuỗi mới được ưu tiên hơn so với mở rộng macro.


1
+1: Rất thú vị, thực sự, nhưng về mặt kỹ thuật phá vỡ yêu cầu không sử dụng bộ xử lý trước. Tuy nhiên, hạn chế đã không có ý định sa thải như vậy thoải mái câu trả lời :)
Armen Tsirunyan

1
Chà, nếu bạn theo dõi hàm với a #undef u8thì việc sử dụng bộ tiền xử lý chỉ có thể quan sát được nếu chương trình của bạn có macro được xác định trước đó có tên u8(boooo). Nếu đó là một mối quan tâm thực sự, điều đó vẫn có thể được giải quyết bằng cách sử dụng các lệnh gọi / lệnh gọi macro push / pop cụ thể cho việc triển khai (tôi tin rằng hầu hết các triển khai đều có những điều này).
James McNellis

3
Một lập luận khá hợp lý là trên hệ thống C ++ 03, ai đó có thể # xác định u8 để cung cấp các khả năng C ++ 0x được mô phỏng. Tuy nhiên, tôi thực sự thích câu trả lời.
Christopher Smith

1
bạn chỉ có thể di chuyển hàm isCpp0x này sang một đơn vị dịch riêng biệt để điều macro này không ảnh hưởng đến mã khác.
unaulunkulu

1
Tôi nghĩ rằng có sự khác biệt giữa việc sử dụng bộ tiền xử lý để dựa vào trình biên dịch cài đặt một số giá trị macro và sử dụng bộ tiền xử lý để phát hiện các tính năng ngôn ngữ thực tế. Đó là lý do tại sao tôi không nghĩ rằng câu trả lời này là gian lận.
Lightness Races in Orbit,

33

Làm thế nào về một séc bằng cách sử dụng các quy tắc mới để >>đóng mẫu:

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

Ngoài ra, hãy kiểm tra nhanh 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 Quả thực là một ý tưởng tuyệt vời :) Tuy nhiên, trong thực tế, điều này sẽ phá vỡ với Visual C ++ 2005/2088 không hỗ trợ C ++ 0x nhưng cho phép >> được sử dụng trong các mẫu theo cách C ++ 0x.
Karel Petranek

4
Ơ ơ; Tôi thích sự lạm dụng ADL! Tuy nhiên, không thể triển khai C ++ 03 phù hợp có một hàm được đặt tên std::move?
James McNellis

1
@FredOverflow: Tôi sẽ không bận tâm. Giao diện người dùng tệ quá!
Các cuộc đua ánh sáng trong quỹ đạo

16

Không giống như C ++ trước đó, C ++ 0x cho phép các kiểu tham chiếu được tạo từ các kiểu tham chiếu nếu kiểu tham chiếu cơ sở đó được đưa vào, ví dụ: tham số mẫu:

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

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

Thật không may, việc chuyển tiếp hoàn hảo đi kèm với cái giá là phá vỡ khả năng tương thích ngược.

Một thử nghiệm khác có thể dựa trên các loại cục bộ hiện được phép làm đối số mẫu:

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

Có lẽ tôi nên đã quay đó vào một lớp học đặc điểm;)
uwedolinsky

1
1 Ý tưởng tốt đẹp, tôi chỉ không chắc chắn nếu isC++0xlà C ++ có giá trị định danh;)
Karel Petranek

1
Tham chiếu tốt cho tham chiếu từ suy luận tham chiếu là gì?
Kerrek SB

@ kerrek-sb: Dự thảo các cuộc đàm phán về vấn đề này trong 8.3.2.6 (Tài liệu tham khảo)
uwedolinsky

15

Đây không phải là một ví dụ hoàn toàn chính xác, nhưng đó là một ví dụ thú vị có thể phân biệt C và C ++ 0x (mặc dù đó là C ++ 03 không hợp lệ):

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

10
Về mặt kỹ thuật, điều này dựa vào sizeof(int) != 1sự thật. Trên hệ thống 0x với các chars đặc biệt lớn , kết quả có thể giống nhau. Tuy nhiên, vẫn là một mẹo nhỏ.
Dennis Zickefoose

@Dennis - charluôn luôn là một byte dù
Node

4
@Node: một byte không phải lúc nào cũng là 8 bit.
Alexandre C.

2
@Node sizeof(char)sẽ luôn là 1, theo định nghĩa. Nhưng CHAR_BIT(được định nghĩa trong giới hạn.h) được phép lớn hơn 8. Do đó, cả hai charintcó thể có 32 bit, trong trường hợp đó là sizeof(int) == 1(và CHAR_BIT == 32).
Sjoerd

12

Từ câu hỏi này :

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

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

Tôi tự hỏi làm thế nào điều này có thể hoạt động; đã thử nó, bây giờ rõ ràng: có một lỗi nhỏ. nó hoạt động nếu bạn thay đổi dòng cuối cùng để:bool is_cpp0x = !test[0].flag;
AWX

1
chính đáng: C ++ 0x cấu trúc mặc định Ttrong khi C ++ cấu trúc 03 bản sao từT()
AWX

9

Mặc dù không quá ngắn gọn ... Trong C ++ hiện tại, bản thân tên mẫu lớp được hiểu là tên kiểu (không phải tên mẫu) trong phạm vi của khuôn mẫu lớp đó. Mặt khác, tên mẫu lớp có thể được sử dụng làm tên mẫu trong 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;
}

Về mặt kỹ thuật, NB kiểm tra thư viện tiêu chuẩn không phải là trình biên dịch và mặc dù nó là C ++ 03 và C ++ 0x hợp lệ, nó không phải là C ++ 98 hợp lệ, vì vậy có thể thực hiện một số chỉnh sửa để phát hiện C ++ 98 / C ++ 03 / C ++ 0x stdlib
Jonathan Wakely
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.