2011-10-03 16 views
14

Tôi kiểm tra mã trong tiêu chuẩn C++ ISO/IEC 14882-03 14.6.1/9 trên Xcode 4.1 và Visual Studio 2008. Kết quả đầu ra của hai trình biên dịch khác với kết quả mong đợi của tiêu chuẩn.Kết quả thực tế của độ phân giải tên trong mẫu lớp khác với tiêu chuẩn C++ 03

Mã được dán bên dưới.

#include <stdio.h> 
#include <iostream> 
using namespace std; 

void f(char); 

template <class T > void g(T t) 
{ 
    f(1); 
    f(T(1)); 
    f(t); 
} 

void f(int); 
void h() 
{ 
    g(2); 
    g('a'); 
} 

void f(int) 
{ 
    cout << "f int" << endl; 
} 


void f(char) 
{ 
    cout << "f char" << endl; 
} 


int main() { 
    h(); 
    return 0; 
} 

Như mô tả tiêu chuẩn. Sản lượng dự kiến ​​phải là

f char 
f int 
f int 
f char 
f char 
f char 

Tạo và chạy mã trên Xcode 4.1. Đầu ra là như dưới đây. Trong các thiết lập xây dựng, tôi đã cố gắng thay đổi "Compiler for C/C++/Object-C" thành Apple LLVM Compiler 2.1, Gcc 4.2 và LLVM GCC 4.2. Các kết quả đầu ra là như nhau.

f char 
f char 
f char 
f char 
f char 
f char 

Tạo và chạy mã trên Microsoft Visual Studio 2008. Kết quả như sau.

f int 
f int 
f int 
f int 
f char 
f char 

Mô tả (14.6.1/9) của tiêu chuẩn được dán bên dưới.

Nếu tên không phụ thuộc vào thông số mẫu (như được định nghĩa trong 14.6.2), một tuyên bố (hoặc tập hợp các tuyên bố) cho tên đó phải nằm trong phạm vi tại điểm tên xuất hiện trong định nghĩa mẫu; tên được gắn kết với khai báo (hoặc khai báo) được tìm thấy tại thời điểm đó và ràng buộc này không bị ảnh hưởng bởi các khai báo có thể nhìn thấy tại điểm khởi tạo. [Ví dụ:

void f(char); 
template<class T> void g(T t) 
{ 
f(1); // f(char) 
f(T(1)); // dependent 
f(t); // dependent 
dd++; // not dependent 
} 
void f(int); 
double dd; 
void h() 
{ 
// error: declaration for dd not found 
g(2); // will cause one call of f(char) followed // by two calls of f(int) 
g(’a’); // will cause three calls of f(char) 

-end dụ]

Mã này được tốt được hình thành với các trình biên dịch, nhưng những kết quả khác nhau. Sẽ rất nguy hiểm khi chuyển mã này sang các nền tảng khác nhau.

Ai đó có lý do tại sao những trình biên dịch này không tuân theo tiêu chuẩn?

Chỉnh sửa trên 10/11/2011

mỗi http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197, ví dụ trong tiêu chuẩn là sai. Tôi kiểm tra mã dưới đây trên Clang và Gcc.

#include <stdio.h> 
#include <iostream> 
using namespace std; 

void f(char); 

template <class T > void g(T t) 
{ 
    f(1); 
    f(T(1)); 
    f(t); 
} 

enum E{ e }; 

void f(E); 
void h() 
{ 
    g(e); 
    g('a'); 
} 

void f(E) 
{ 
    cout << "f E" << endl; 
} 

void f(char) 
{ 
    cout << "f char" << endl; 
} 

int main() { 
    h(); 
    return 0; 
} 

Kết quả như mong đợi.

f char 
f E 
f E 
f char 
f char 
f char 

Cảm ơn,

Jeffrey

+2

+1 cho câu hỏi đầu tiên tuyệt vời –

+0

Điều thú vị là Clang 2.9 dường như có cùng vấn đề với gcc. –

Trả lời

3

Như đã nêu trong ví dụ đầu tiên, đây là ví dụ về tra cứu tên hai pha, cả GCC và Clang đều thực hiện nhưng MSVC thì không. Và trong trường hợp này, cả GCC và Clang đều đúng: nó thực sự là chuẩn sai, như đã nói trong C++ core defect report #197. Tiêu chuẩn C++ 11 chứa một ví dụ khác.

Đây là một trong số most common problems chúng tôi thấy khi chuyển mã sang Clang từ MSVC (không bao giờ triển khai tra cứu tên hai giai đoạn) hoặc từ GCC (không thực hiện tra cứu tên hai pha đồng nhất cho đến gần đây).

2

Tôi không biết phải nói với bạn ngoại trừ việc tôi sẽ đồng ý với bạn rằng đây là hành vi không chính xác. Tôi nghĩ rằng những gì có thể xảy ra là trong trường hợp của MSVC, trình biên dịch tối ưu hóa thêm một vượt qua với chi phí kết thúc với kiến ​​thức về một hàm được định nghĩa sau mà không nên sử dụng trong trường hợp không -các cuộc gọi điện thoại. Tôi phải thú nhận, tôi không hiểu làm thế nào GCC/LLVM sẽ kết thúc với kết quả mà họ làm, vì kết quả là những gì bạn mong đợi là ngoại lệ và không phải là quy tắc.

Tôi đoán tôi sẽ báo cáo lỗi này là một lỗi trên http://bugreport.apple.com/http://connect.microsoft.com/ và xem họ nói gì?

+0

Cảm ơn, Mahmoud. Vui lòng cập nhật cho tôi nếu có bất kỳ nhận xét nào. – Jeffrey

+1

@ Jeffrey: Tôi nghĩ anh ta đã gợi ý rằng bạn nên báo lỗi. :) –

+0

g ++ 4.6.1 thể hiện hành vi tương tự như Xcode (trong đó có g ++ 4.2 tương đối cũ), vì vậy hãy gửi báo cáo lỗi tại http://gcc.gnu.org/bugzilla/. – zwol

5

Những gì bạn đang chạy vào thực tế là Visual Studio không triển khai two-phase lookup. Họ chỉ tra cứu tên thật khi bạn tạo mẫu.

Và Microsoft đã quyết định khá nhiều vào thời điểm này rằng họ không quan tâm đến việc hỗ trợ tra cứu hai pha.

+0

MSVC là một nguyên nhân bị mất, tuy nhiên tôi ngạc nhiên rằng gcc và Clang sẽ làm cho nó sai. –