2012-08-09 20 views
64

Trong đoạn mã bên dưới, tôi xác định hàm log tầm thường. Trong main Tôi thử không phải để gọi; Tôi gọi số std::log. Tuy nhiên, riêng của tôi log được gọi là; và tôi thấy "log!" trên màn hình. Có ai biết tại sao không? Tôi sử dụng G ++ 4.7 và clang ++ 3.2.Tại sao nhật ký của tôi trong không gian tên std?

#include <iostream> 
#include <cmath> 

double log(const double x) { std::cout << "log!\n"; return x; } 

int main(int argc, char *argv[]) 
{ 
    std::log(3.14); 
    return 0; 
} 
+10

âm thanh như một lỗi biên dịch nghiêm trọng ... – MFH

+0

tôi có thể tái sản xuất này trên g ++ 4.6 dưới Macports. Tuy nhiên, nó không xảy ra trong g ++ 4.2 hoặc 4.4. – carlosdc

+1

http://codepad.org/Uwhgrv7q http://codepad.org/z07Ctfyn Frome hai tôi sẽ nói rằng hàm std :: log() gọi log().nhưng sau đó nó sẽ tạo ra một lỗi/cảnh báo rằng tập tin của bạn xác định lại nhật ký hoặc một cái gì đó như t – Gir

Trả lời

53

C++ chuẩn 17.6.1.2 đoạn 4 (tôi nhấn mạnh):

Trừ như đã nêu tại các khoản 18 đến 30 và Phụ lục D, nội dung của mỗi tiêu đề cname sẽ là tương tự như của tiêu đề tương ứng name.h, như được quy định trong thư viện chuẩn C (1.2) hoặc C Unicode TR, nếu thích hợp, như thể bao gồm. Tuy nhiên, trong thư viện chuẩn C++, các khai báo (ngoại trừ các tên được định nghĩa là các macro trong C) nằm trong phạm vi không gian tên (3.3.6) của không gian tên std. Nó không được chỉ định cho dù những tên này lần đầu tiên được khai báo trong phạm vi không gian tên toàn cầu và sau đó được tiêm vào không gian tên std bằng cách khai báo sử dụng rõ ràng (7.3.3).

g ++ làm cách thứ hai sao cho một số tệp tiêu đề giống nhau có thể được sử dụng lại cho C và C++. Vì vậy, g ++ được phép khai báo và xác định double log(double) trong không gian tên chung.

mục 17.6.4.3.3 đoạn 3 và 4:

Mỗi tên từ thư viện chuẩn C tuyên bố với mối liên hệ bên ngoài được dành riêng để thực hiện để sử dụng như một tên với extern "C" liên kết, cả trong không gian tên std và trong không gian tên chung.

Mỗi chữ ký hàm từ thư viện chuẩn C được khai báo với liên kết bên ngoài được dành riêng cho việc triển khai để sử dụng làm chữ ký hàm với cả liên kết extern "C"extern "C++" hoặc tên của phạm vi không gian tên trong không gian tên chung.

Và lên ở phía trên cùng của Mục 17.6.4.3 đoạn 2:

Nếu một chương trình tuyên bố hoặc định nghĩa một tên trong một bối cảnh mà nó được dành riêng, khác hơn là cho phép một cách rõ ràng bởi khoản này, hành vi của nó là không xác định.

Mặt khác, bạn có thể không khai báo hoặc xác định ::log bằng bất kỳ cách nào.

Quá xấu, chuỗi công cụ g ++ không cung cấp cho bạn bất kỳ thông báo lỗi nào.

+6

+1: Đây là câu trả lời hay nhất. –

8

Điều gì xảy ra, tôi mong đợi, là std::log chỉ cần ủy quyền cho ::log. Thật không may, ::log chỉ cung cấp sự quá tải float và bạn vui lòng cung cấp sự quá tải double, giúp bạn trở thành đối sánh tốt hơn. Nhưng tôi vẫn không thấy nó được xem như thế nào trong bộ quá tải.

+0

'' là bắt buộc để cung cấp 'log (double)'. – aschepler

+1

Tôi mong đợi tương tự trên MSVC10 và tôi _eventually_ đã nhận được một lỗi biểu tượng trùng lặp trên liên kết nhưng chỉ sau 3 hoặc 4 xây dựng lại. –

+0

@aschepler: '' là bắt buộc để cung cấp 'double std :: log (double)' (tôi đang tập trung vào không gian tên một vấn đề lớn, bởi vì nó là ngay bây giờ). Tiêu chuẩn yêu cầu chúng phải nằm trong không gian tên ':: std', và mỗi việc thực hiện đều miễn phí để định nghĩa chúng trong không gian tên chung nếu chúng chọn. – Cornstalks

8

On libstdC++ 's cmath bạn sẽ thấy điều này:

using ::log; 

Vì vậy, nó được đưa vào các chức năng math.h từ không gian tên toàn cầu vào std. Rất tiếc, bạn đang cung cấp triển khai cho double log(double), vì vậy trình liên kết sẽ không sử dụng tệp đó từ lib toán học. Vì vậy, chắc chắn một lỗi trong libstdC++ .

EDIT: Tôi cho rằng đó là lỗi trong libstdC++ vì std::log không bị nhiễu sóng với thư viện C khi bạn yêu cầu rõ ràng phiên bản std::. Tất nhiên, cách này để ghi đè các chức năng thư viện chuẩn là một "tính năng" cũ từ ngôn ngữ C.

EDIT 2: Tôi phát hiện ra rằng tiêu chuẩn không thực sự cấm mang tên từ không gian tên chung vào std. Vì vậy, không phải là một lỗi sau khi tất cả, chỉ là một hệ quả của các chi tiết thực hiện.

+0

Tuyên bố 'using' chỉ nên nhập các khai báo có thể nhìn thấy tại điểm đó, tôi nghĩ, điều đó có nghĩa là một hàm khai báo sau đó sẽ không được nhập theo cách này. – bames53

+0

Tôi đã chỉnh sửa câu trả lời của mình, tiêu chuẩn cho phép loại triển khai này, lần đầu tiên khai báo các hàm math.h, sau đó đưa các phiên bản 'double' vào' std'. – DanielKO

6

Trong C++, trình biên dịch là miễn phí để triển khai thư viện C trong không gian tên chung và ủy quyền cho nó (đây là việc triển khai đã được xác định).

17.6.1.2.4 Trừ như đã nêu tại các khoản 18 đến 30 và Phụ lục D, nội dung của mỗi cname tiêu đề phải giống như của header name.h tương ứng, như ed fi Speci trong tiêu chuẩn C thư viện (1.2) hoặc C Unicode TR, nếu thích hợp, như thể bao gồm. Tuy nhiên, trong thư viện chuẩn C++, các khai báo (ngoại trừ các tên được định nghĩa là các macro trong C) nằm trong phạm vi không gian tên (3.3.6) của vùng tên không gian. Đó là không xác định liệu những tên này được khai báo lần đầu trong phạm vi không gian tên chung và sau đó được tiêm vào không gian tên bằng cách khai báo sử dụng rõ ràng (7.3.3).

Nói chung, tôi muốn tránh tạo một hàm có chữ ký giống với một trong các thư viện chuẩn của C. Tiêu chuẩn C++ chắc chắn cho phép các trình biên dịch tự do sử dụng các chữ ký này nếu nó chọn, có nghĩa là bạn có thể đang chiến đấu với trình biên dịch của mình nếu bạn cố gắng sử dụng các chữ ký giống nhau. Do đó, bạn nhận được kết quả lạ.

Tôi mong đợi lỗi liên kết hoặc cảnh báo và tôi nghĩ rằng có thể đáng báo cáo điều này.

[sửa]

Wow, ninja'd.

+0

Chúc may mắn lần sau :) – user2023370

0

Vì bạn đã ghi đè nó trong không gian tên chung. Sử dụng một không gian tên tránh nguy hiểm đó nếu bạn không muốn chuyển sang một ngôn ngữ an toàn hơn, sạch hơn như ví dụ Nim.

Proper use of namespace demo:

#include <iostream> 
#include <cmath> // Uses ::log, which would be the log() here if it were not in a namespace, see http://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace 

// Silently overrides std::log 
//double log(double d) { return 420; } 

namespace uniquename { 
    using namespace std; // So we don't have to waste space on std:: when not needed. 

    double log(double d) { 
     return 42; 
    } 

    int main() { 
     cout << "Our log: " << log(4.2) << endl; 
     cout << "Standard log: " << std::log(4.2); 
     return 0; 
    } 
} 

// Global wrapper for our contained code. 
int main() { 
    return uniquename::main(); 
} 

Output:

Our log: 42 
Standard log: 1.43508 
Các vấn đề liên quan