Mã C ++ có thể hợp lệ trong cả C ++ 03 và C ++ 11 nhưng làm những việc khác nhau không?


298

Có thể mã C ++ phù hợp với cả tiêu chuẩn C ++ 03 và tiêu chuẩn C ++ 11 , nhưng thực hiện các điều khác nhau tùy thuộc vào tiêu chuẩn nào được biên dịch?


26
Tôi khá chắc chắn autocó thể dẫn đến một tình huống như thế này
OMGtechy

8
Đúng. Một ví dụ là >>khi được sử dụng trong một mẫu. Bạn có thể đưa ra một tình huống mà nó có thể biên dịch cho cả hai tiêu chuẩn. Một cái khác mà tôi chắc chắn sẽ dễ dàng tìm thấy các thay đổi là trong khởi tạo.
chris

5
Đây là một bài viết hay về tình huống >> gustt.wordpress.com/2013/12/15/15
chris

6
@OMGtechy: Tôi không nghĩ auto có thể gây ra điều này. Với nghĩa cũ, một autokhai báo yêu cầu một tên loại; với ý nghĩa mới, tên loại không được phép.
Keith Thompson

2
Làm thế nào là nó kết thúc mở? Chính bạn đã chỉ ra thông qua một câu hỏi khác rằng câu trả lời cho câu hỏi này là "có, đây là một ví dụ về cách thức". Có một câu trả lời rất chắc chắn cho câu hỏi, như chính bạn đã chỉ ra.
jalf

Câu trả lời:


283

câu trả lời chắc chắn là đúng. Về mặt tích cực có:

  • Mã mà trước đây các đối tượng được sao chép ngầm sẽ di chuyển chúng khi có thể.

Về mặt tiêu cực, một số ví dụ được liệt kê trong phụ lục C của tiêu chuẩn. Mặc dù có nhiều tiêu cực hơn tích cực, nhưng mỗi một trong số chúng ít có khả năng xảy ra hơn.

Chuỗi ký tự

#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"

#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal

Chuyển đổi loại 0

Trong C ++ 11, chỉ có các chữ là hằng số con trỏ null nguyên:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
    f(0*N); // Calls #2; used to call #1
}

Kết quả làm tròn sau khi chia số nguyên và modulo

Trong C ++ 03, trình biên dịch được phép làm tròn về 0 hoặc hướng tới vô cực âm. Trong C ++ 11, bắt buộc phải làm tròn về 0

int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0

Khoảng trắng giữa các dấu ngoặc đóng mẫu lồng nhau >> vs >>

Bên trong một chuyên môn hóa hoặc khởi tạo, >>thay vào đó có thể được hiểu là một sự thay đổi bên phải trong C ++ 03. Đây là nhiều khả năng để phá vỡ mã hiện tại mặc dù: (từ http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )

template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);

void total(void) {
    // fon<fun<9> >(1) >> 2 in both standards
    unsigned int A = fon< fun< 9 > >(1) >>(2);
    // fon<fun<4> >(2) in C++03
    // Compile time error in C++11
    unsigned int B = fon< fun< 9 >>(1) > >(2);
}

Toán tử newbây giờ có thể ném ngoại lệ khác hơnstd::bad_alloc

struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
    foo *f = new foo();
} catch (std::bad_alloc &) {
    // c++03 code
} catch (std::exception &) {
    // c++11 code
}

Các hàm hủy do người dùng khai báo có một ví dụ đặc tả ngoại lệ ngầm định từ Thay đổi vi phạm nào được giới thiệu trong C ++ 11?

struct A {
    ~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try { 
    A a; 
} catch(...) { 
    // C++03 will catch the exception
} 

size() bây giờ cần có các container để chạy trong O (1)

std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03

std::ios_base::failurekhông xuất phát trực tiếp từ std::exceptionnữa

Trong khi lớp cơ sở trực tiếp là mới, std::runtime_errorthì không. Như vậy:

try {
    std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
    std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
    std::cerr << "Pre-C++11\n";
}

11
Đẹp, +1. Một điều nữa là người dùng đã khai báo hàm hủy bây giờ hoàn toàn noexecpt(true)do đó throwtrong một hàm hủy sẽ gọi std::terminate. Nhưng tôi hy vọng bất cứ ai viết mã như vậy sẽ hài lòng về điều này!
typ1232

4
Nhưng std :: system_error tự nó (gián tiếp) có nguồn gốc từ std :: ngoại lệ, vì vậy catch (std::exception &)vẫn bắt được std::ios_base::failure.
user2665887

@ user2665887 bạn nói đúng. nó vẫn có thể ảnh hưởng đến hành vi của một chương trình, nhưng tôi không thể nghĩ ra một ví dụ tối thiểu ngay bây giờ.
ví dụ

4
Tôi rất bối rối, vì những gì bạn nói operator newlà chính xác (bây giờ nó có thể ném std::bad_array_new_length), nhưng ví dụ của bạn hoàn toàn không cho thấy điều đó. Mã bạn hiển thị giống nhau trong C ++ 03 và C ++ 11 AFAIK.
Vịt Mooing

2
Mặt trái của danh sách :: kích thước là O (1) là mối nối bây giờ là O (n)
Tony Delroy

55

Tôi chỉ cho bạn bài viết nàyphần tiếp theo , trong đó có một ví dụ hay về cách >>có thể thay đổi ý nghĩa từ C ++ 03 sang C ++ 11 trong khi vẫn biên dịch cả hai.

bool const one = true;
int const two = 2;
int const three = 3;

template<int> struct fun {
    typedef int two;
};

template<class T> struct fon {
    static int const three = ::three;
    static bool const one = ::one;
};

int main(void) {
    fon< fun< 1 >>::three >::two >::one; // valid for both  
}

Phần quan trọng là dòng trong main, là một biểu thức.

Trong C ++ 03:

1 >> ::three = 0
=> fon< fun< 0 >::two >::one;

fun< 0 >::two = int
=> fon< int >::one

fon< int >::one = true
=> true

Trong C ++ 11

fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one

::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false

Xin chúc mừng, hai kết quả khác nhau cho cùng một biểu thức. Cấp, C ++ 03 một đã đưa ra một mẫu cảnh báo Clang khi tôi thử nghiệm nó.


nó là lạ mà nó không đòi hỏi typenamecho ::twobằng C ++ 03 phiên bản
Zahir

3
Đẹp một, làm cho nó sôi lên để đánh giá truehoặc falsecho các tiêu chuẩn khác nhau. Có lẽ chúng ta có thể sử dụng nó như một thử nghiệm tính năng </ đùa>
cmaster - phục hồi monica

@zahir, Nó không phải là một loại, chỉ là một giá trị.
chris

tốt, các tùy chọn cmdline thích hợp cảnh báo về điều này ( warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]), nhưng vẫn là một ví dụ hay về cách ::toán tử mơ hồ thay đổi ý nghĩa (tham chiếu đến phạm vi toàn cầu hoặc hội nghị người đứng trước nó)
ví dụ

@example, Thật đáng ngạc nhiên, GCC đưa ra cảnh báo đó, nhưng Clang thì không.
chris

39

Có, có một số thay đổi sẽ khiến cùng một mã dẫn đến hành vi khác nhau giữa C ++ 03 và C ++ 11. Sự khác biệt về quy tắc giải trình tự tạo ra một số thay đổi thú vị bao gồm một số hành vi chưa được xác định trước đó trở nên được xác định rõ.

1. nhiều đột biến của cùng một biến trong danh sách khởi tạo

Một trường hợp góc rất thú vị sẽ có nhiều đột biến của cùng một biến trong danh sách khởi tạo, ví dụ:

int main()
{
    int count = 0 ;
    int arrInt[2] = { count++, count++ } ;

    return 0 ;
}

Trong cả C ++ 03 và C ++ 11, điều này được xác định rõ nhưng thứ tự đánh giá trong C ++ 03 là không xác định nhưng trong C ++ 11, chúng được đánh giá theo thứ tự xuất hiện . Vì vậy, nếu chúng tôi biên dịch bằng clangchế độ C ++ 03, nó sẽ đưa ra cảnh báo sau ( xem trực tiếp ):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]

    int arrInt[2] = { count++, count++ } ;

                           ^        ~~

nhưng không cung cấp cảnh báo trong C ++ 11 ( xem trực tiếp ).

2. Quy tắc sắp xếp mới làm cho i = ++ i + 1; được xác định rõ trong C ++ 11

Các quy tắc giải trình tự mới được thông qua sau C ++ 03 có nghĩa là:

int i = 0 ;
i = ++ i + 1;

không còn là hành vi không xác định trong C ++ 11, điều này được đề cập trong báo cáo lỗi 637. Quy tắc tuần tự và ví dụ không đồng ý

3. Quy tắc sắp xếp mới cũng làm cho ++++ i; được xác định rõ trong C ++ 11

Các quy tắc giải trình tự mới được thông qua sau C ++ 03 có nghĩa là:

int i = 0 ;
++++i ;

không còn là hành vi không xác định trong C ++ 11.

4. Các ca trái được ký hợp lý hơn một chút

Các bản nháp sau này của C ++ 11 bao gồm N3485những gì tôi liên kết bên dưới đã cố định hành vi không xác định của việc chuyển 1 bit vào hoặc qua bit dấu . Điều này cũng được đề cập trong báo cáo lỗi 1457 . Howard Hinnant đã nhận xét về tầm quan trọng của sự thay đổi này trong luồng trên Dịch chuyển trái (<<) có phải là số nguyên âm không xác định hành vi trong C ++ 11 không? .

5. Hàm constexpr có thể được coi là biểu thức hằng số thời gian biên dịch trong C ++ 11

C ++ 11 đã giới thiệu các hàm constexpr trong đó:

Trình xác định constexpr tuyên bố rằng có thể đánh giá giá trị của hàm hoặc biến tại thời gian biên dịch. Các biến và hàm như vậy sau đó có thể được sử dụng khi chỉ cho phép biên dịch các biểu thức hằng số thời gian.

trong khi C ++ 03 không có tính năng constexpr, chúng tôi không phải sử dụng rõ ràng từ khóa constexpr vì thư viện chuẩn cung cấp nhiều chức năng trong C ++ 11 dưới dạng constexpr . Ví dụ: std :: num_limits :: min . Điều này có thể dẫn đến hành vi khác nhau, ví dụ:

#include <limits>

int main()
{
    int x[std::numeric_limits<unsigned int>::min()+2] ;
}

Sử dụng clangtrong C ++ 03, điều này sẽ gây ra xmột mảng có chiều dài thay đổi, là một phần mở rộng và sẽ tạo ra cảnh báo sau:

warning: variable length arrays are a C99 feature [-Wvla-extension]
    int x[std::numeric_limits<unsigned int>::min()+2] ;
         ^

trong khi trong C ++ 11 std::numeric_limits<unsigned int>::min()+2là biểu thức hằng số thời gian biên dịch và không yêu cầu mở rộng VLA.

6. Trong C ++, các đặc tả ngoại lệ không ngoại lệ được tạo hoàn toàn cho các hàm hủy của bạn

Vì trong C ++ 11 hàm hủy được xác định bởi người dùng có noexcept(true)đặc tả ngầm định như được giải thích trong các hàm hủy không có ngoại lệ, điều đó có nghĩa là chương trình sau:

#include <iostream>
#include <stdexcept>

struct S
{
  ~S() { throw std::runtime_error(""); } // bad, but acceptable
};

int main()
{
  try { S s; }
  catch (...) {
    std::cerr << "exception occurred";
  } 
 std::cout << "success";
}

Trong C ++ 11 sẽ gọi std::terminatenhưng sẽ chạy thành công trong C ++ 03.

7. Trong C ++ 03, các đối số mẫu không thể có liên kết nội bộ

Điều này được trình bày độc đáo tại sao std :: sort không chấp nhận So sánh các lớp được khai báo trong một hàm . Vì vậy, đoạn mã sau không nên hoạt động trong C ++ 03:

#include <iostream>
#include <vector>
#include <algorithm>

class Comparators
{
public:
    bool operator()(int first, int second)
    {
        return first < second;
    }
};

int main()
{
    class ComparatorsInner : public Comparators{};

    std::vector<int> compares ;
    compares.push_back(20) ;
    compares.push_back(10) ;
    compares.push_back(30) ;

    ComparatorsInner comparatorInner;
    std::sort(compares.begin(), compares.end(), comparatorInner);

    std::vector<int>::iterator it;
    for(it = compares.begin(); it != compares.end(); ++it)
    {
        std::cout << (*it) << std::endl;
    }
}

nhưng hiện tại clangcho phép mã này ở chế độ C ++ 03 với một cảnh báo trừ khi bạn sử dụng -pedantic-errorscờ, loại icky, xem nó trực tiếp .

8. >> không còn hình thành khi đóng nhiều mẫu

Việc sử dụng >>để đóng nhiều mẫu không còn được định hình nữa mà có thể dẫn đến mã với các kết quả khác nhau trong C ++ 03 và C + 11. Ví dụ dưới đây được lấy từ dấu ngoặc vuông và khả năng tương thích ngược :

#include <iostream>
template<int I> struct X {
  static int const c = 2;
};
template<> struct X<0> {
  typedef int c;
};
template<typename T> struct Y {
  static int const c = 3;
};
static int const c = 4;
int main() {
  std::cout << (Y<X<1> >::c >::c>::c) << '\n';
  std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}

và kết quả trong C ++ 03 là:

0
3

và trong C ++ 11:

0
0

9. C ++ 11 thay đổi một số hàm tạo std :: vector

Mã được sửa đổi một chút từ câu trả lời này cho thấy rằng sử dụng hàm tạo sau từ std :: vector :

std::vector<T> test(1);

tạo ra các kết quả khác nhau trong C ++ 03 và C ++ 11:

#include <iostream>
#include <vector>

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


int main()
{
    std::vector<T> test(1);
    bool is_cpp11 = !test[0].flag;

    std::cout << is_cpp11 << std::endl ;
}

10. Thu hẹp chuyển đổi trong tổng hợp khởi tạo

Trong C ++ 11, một chuyển đổi thu hẹp trong các trình khởi tạo tổng hợp không được định dạng và có vẻ như gcccho phép điều này trong cả C ++ 11 và C ++ 03 mặc dù nó cung cấp một cảnh báo theo mặc định trong C ++ 11:

int x[] = { 2.0 };

Này được đề cập trong dự thảo C ++ 11 phần chuẩn 8.5.4 List-khởi đoạn 3 :

Danh sách khởi tạo một đối tượng hoặc tham chiếu loại T được định nghĩa như sau:

và chứa viên đạn sau ( nhấn mạnh của tôi ):

Mặt khác, nếu T là một loại lớp, các hàm tạo được xem xét. Các hàm tạo có thể áp dụng được liệt kê và hàm tốt nhất được chọn thông qua độ phân giải quá tải (13.3, 13.3.1.7). Nếu một chuyển đổi thu hẹp (xem bên dưới) là bắt buộc để chuyển đổi bất kỳ đối số nào, thì chương trình không được định dạng

Này và nhiều ví dụ hơn được bao gồm trong dự thảo chuẩn C ++ phần annex C.2 C ++ và ISO C ++ 2003 . Nó cũng bao gồm:

  • Các loại chuỗi ký tự mới [...] Cụ thể, các macro có tên R, u8, u8R, u, uR, U, UR hoặc LR sẽ không được mở rộng khi liền kề với một chuỗi ký tự mà sẽ được hiểu là một phần của chuỗi ký tự . Ví dụ

    #define u8 "abc"
    const char *s = u8"def"; // Previously "abcdef", now "def"
  • Hỗ trợ chuỗi bằng chữ do người dùng định nghĩa [...] Trước đây, # 1 sẽ bao gồm hai mã thông báo tiền xử lý riêng biệt và macro _x sẽ được mở rộng. Trong Tiêu chuẩn quốc tế này, # 1 bao gồm một mã thông báo tiền xử lý duy nhất, vì vậy macro không được mở rộng.

    #define _x "there"
    "hello"_x // #1
  • Chỉ định làm tròn cho kết quả của số nguyên / và% [...] 2003 sử dụng phép chia số nguyên làm tròn kết quả về 0 hoặc hướng tới vô cực âm, trong khi Tiêu chuẩn quốc tế này luôn làm tròn kết quả về 0.

  • Độ phức tạp của các hàm thành viên kích thước () không đổi [...] Một số cách triển khai vùng chứa phù hợp với C ++ 2003 có thể không phù hợp với các yêu cầu kích thước () đã chỉ định trong Tiêu chuẩn quốc tế này. Điều chỉnh các thùng chứa như std :: list theo các yêu cầu khắt khe hơn có thể yêu cầu thay đổi không tương thích.

  • Thay đổi lớp cơ sở của std :: ios_base :: fail [...] std :: ios_base :: fail không còn xuất phát trực tiếp từ std :: ngoại lệ, nhưng bây giờ được lấy từ std :: system_error, từ đó có nguồn gốc từ std :: runtime_error. Mã C ++ 2003 hợp lệ giả định rằng std :: ios_base :: fail được lấy trực tiếp từ std :: ngoại lệ có thể thực thi khác trong Tiêu chuẩn quốc tế này.


Vì vậy, hầu hết các ví dụ thu hẹp đến thực tế là hành vi không xác định trước đây bây giờ được xác định rõ?
MatthiasB

@MatthiasB 2, 3 và 4 là về điều này vì vậy tại thời điểm này chúng không còn là đa số của các ví dụ nữa. Tôi nghi ngờ tôi sẽ tìm thấy nhiều ví dụ hành vi không xác định hơn để tôi thêm nhiều hơn thì chúng sẽ trở thành một tập nhỏ hơn.
Shafik Yaghmour

Chà, hành vi số 1 là không xác định, vì vậy tôi sẽ coi đó là hành vi không xác định (ít nhất bạn không thể mong đợi nhận được kết quả cụ thể với c ++ 03, bây giờ với c ++ 11 bạn có thể), # 5 sử dụng không mở rộng tiêu chuẩn của c ++. Nhưng tôi đoán bạn đúng. Bạn càng tìm kiếm nó, bạn càng tìm thấy nhiều ví dụ được xác định trong cả hai tiêu chuẩn nhưng tạo ra kết quả khác nhau.
MatthiasB

@MatthiasB có, cả hành vi không xác định và không xác định đều có kết quả không mong muốn. Đối với các phần mở rộng xem xét Linux phụ thuộc vào một số phần mở rộng gcc mà chúng ta nên giả sử trong thế giới thực mà chúng quan trọng. Tôi đã không mong đợi để tìm thấy rất nhiều ví dụ khi lần đầu tiên tôi trả lời câu hỏi này.
Shafik Yaghmour

35

Một thay đổi không tương thích ngược nguy hiểm tiềm tàng là trong các hàm tạo của các thùng chứa trình tự std::vector, như cụ thể là trong tình trạng quá tải chỉ định kích thước ban đầu. Trong C ++ 03, họ đã sao chép một phần tử được xây dựng mặc định, trong C ++ 11, họ mặc định xây dựng từng phần tử.

Xem xét ví dụ này (sử dụng boost::shared_ptrsao cho C ++ 03 hợp lệ):

#include <deque>
#include <iostream>

#include "boost/shared_ptr.hpp"


struct Widget
{
  boost::shared_ptr<int> p;

  Widget() : p(new int(42)) {}
};


int main()
{
  std::deque<Widget> d(10);
  for (size_t i = 0; i < d.size(); ++i)
    std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n';
}

Ví dụ trực tiếp C ++ 03

Ví dụ trực tiếp C ++ 11

Lý do là C ++ 03 đã chỉ định một tình trạng quá tải cho cả "chỉ định kích thước và phần tử nguyên mẫu" và "chỉ xác định kích thước", như thế này (đối số cấp phát được bỏ qua cho ngắn gọn):

container(size_type size, const value_type &prototype = value_type());

Điều này sẽ luôn luôn sao chép prototypevào sizethời gian container . Khi được gọi chỉ với một đối số, do đó nó sẽ tạo các sizebản sao của một phần tử được xây dựng mặc định.

Trong C ++ 11, chữ ký hàm tạo này đã bị xóa và được thay thế bằng hai lần quá tải này:

container(size_type size);

container(size_type size, const value_type &prototype);

Cái thứ hai hoạt động như trước, tạo ra các sizebản sao của prototypephần tử. Tuy nhiên, cái đầu tiên (hiện xử lý các cuộc gọi chỉ với đối số kích thước được chỉ định) mặc định - xây dựng từng phần tử riêng lẻ.

Tôi đoán vì lý do của sự thay đổi này là quá tải C ++ 03 sẽ không thể sử dụng được với loại phần tử chỉ di chuyển. Nhưng đó là một sự thay đổi không hề nhỏ, và một điều hiếm khi được ghi nhận ở đó.


3
Trong khi đây rõ ràng là một sự thay đổi đột phá, tôi thích hành vi của C ++ 11. Tôi hy vọng điều này sẽ dẫn đến việc dequenắm giữ mười vật dụng riêng biệt, chứ không phải mười vật dụng chia sẻ cùng một tài nguyên.
Đặc vụ

19

Kết quả của một lần đọc thất bại từ một std::istreamđã thay đổi. CppReference tóm tắt nó độc đáo:

Nếu trích xuất không thành công (ví dụ: nếu một chữ cái được nhập vào nơi có chữ số dự kiến), valuesẽ không được sửa đổi và failbitđược đặt. (cho đến C ++ 11)

Nếu trích xuất thất bại, zero được ghi vào valuefailbitđược đặt. Nếu trích xuất dẫn đến giá trị quá lớn hoặc quá nhỏ để phù hợp value, std::numeric_limits<T>::max()hoặc std::numeric_limits<T>::min()được viết và failbitcờ được đặt. (kể từ C ++ 11)

Đây chủ yếu là một vấn đề nếu bạn đã quen với ngữ nghĩa mới và sau đó phải viết bằng C ++ 03. Sau đây không phải là thực hành đặc biệt tốt nhưng được xác định rõ trong C ++ 11:

int x, y;
std::cin >> x >> y;
std::cout << x + y;

Tuy nhiên, trong C ++ 03, đoạn mã trên sử dụng một biến chưa được khởi tạo và do đó có hành vi không xác định.


4
Bạn có thể thêm, rằng trong C ++ 03, người ta có thể đã sử dụng hành vi được tiêu chuẩn hóa này để cung cấp một giá trị mặc định, như trong int x = 1, y = 1; cin >> x >> y; cout << x*y;. Với C ++ 03, điều này sẽ tạo ra chính xác xkhi không ythể đọc được.
cmaster - phục hồi monica

15

Chủ đề này Có thể phát hiện sự khác biệt nào, nếu có, giữa C ++ 03 và C ++ 0x trong thời gian chạy có các ví dụ (được sao chép từ luồng đó) để xác định sự khác biệt về ngôn ngữ, ví dụ bằng cách khai thác thu gọn tham chiếu C ++ 11:

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

bool isCpp11() 
{
    int v = 1;
    return f<int&>(v); 
}

và c ++ 11 cho phép các kiểu cục bộ làm tham số mẫu:

template <class T> bool cpp11(T)  {return true;} //T cannot be a local type in C++03
                   bool cpp11(...){return false;}

bool isCpp0x() 
{
   struct local {} var; //variable with local type
   return cpp11(var);
}

7

Đây là một ví dụ khác:

#include <iostream>

template<class T>
struct has {
  typedef char yes;
  typedef yes (&no)[2];    
  template<int> struct foo;    
  template<class U> static yes test(foo<U::bar>*);      
  template<class U> static no  test(...);    
  static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};

enum foo { bar };

int main()
{
    std::cout << (has<foo>::value ? "yes" : "no") << std::endl;
}

Bản in:

Using c++03: no
Using c++11: yes

Xem kết quả trên Coliru

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.