2012-03-15 26 views
9

sau đây C++ mã không biên dịch:Tại sao iostream định nghĩa hàm abs và làm thế nào tôi có thể dừng nó?

int main() { 
    double a = abs(5.1); 
    return 0; 
} 

Nó phàn nàn rằng abs không được định nghĩa, tất nhiên. Nhưng những điều sau đây không biên dịch:

#include <iostream> 

int main() { 
    std::cout << abs(5.1) << std::endl; 
    std::cout << abs(-5.1) << std::endl; 
    return 0; 
} 

Kết quả là hai 5 (không phải là 5.1). Điều này là xấu vì nhiều lý do. Đầu tiên, abs là một chức năng tự nhiên và phổ biến mà tôi sử dụng nó mọi lúc, nhưng phần int hầu như không bao giờ là thứ tôi muốn trả về. Thứ hai, nó quá dễ dàng đối với tôi (hoặc người sử dụng mã của tôi) chỉ cần viết abs và không nhận thấy rằng nó biên dịch nhưng làm điều sai trái, bởi vì tôi (họ) thực sự giỏi trong việc xem cảnh báo. Thứ ba, tôi chỉ đơn giản là không hiểu tại sao iostream làm phiền định nghĩa một hàm abs. Thứ tư, tôi thực sự không hiểu lý do tại sao nó đi vào không gian tên chung.

Có cách nào tôi có thể ngăn chặn chức năng abs có thể bị phản đối này xâm nhập vào không gian tên chung của tôi không?

Nếu vấn đề, tôi đang sử dụng

gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.6) 
+4

Điều này có thể là do ' 'là' # include'-ing '' đằng sau hậu trường. –

+3

Điều này không nên biên dịch. Không nên có hàm 'abs' trong không gian tên chung khi bạn bao gồm' '(không biên dịch trong g ++ 4.6.2 và http://ideone.com/QP6cj). – Zeta

+1

@ Zeta - nó * không * biên dịch và nhập 'abs' vào không gian tên chung bằng trình biên dịch và hệ điều hành mà anh ta có, tôi đã thử nghiệm để đảm bảo. Đó là Xcode hiện tại của Apple, vì vậy việc nâng cấp không đơn giản. –

Trả lời

11

Nhiều khả năng iostream bao gồm stdlib.h để làm một số công việc của mình. Đây là phiên bản C của tiêu đề chỉ tuyên bố abs cho int chỉ trong không gian tên chung (trong C bạn phải sử dụng fabs cho các giá trị double).

Tôi không biết về bất kỳ cách cụ thể để giữ abs khỏi bị bao gồm cách mà nhưng tôi biết rằng g ++ 4.5 là nhiều tốt hơn không có những thứ dư thừa đưa vào bởi cơ bản bao gồm như iostreamstring.

Cũng có thể nhận được cảnh báo rằng đôi bị cắt ngắn thành int (EDIT: có, sử dụng -Wconversion để cảnh báo).

+0

Vâng, tôi biết về cảnh báo, nhưng - như tôi đã ám chỉ trong câu hỏi - tôi sẽ có người dùng mở rộng mã của tôi và tôi chắc chắn họ sẽ tắt cảnh báo hoặc chỉ bỏ qua chúng. Và sau đó vấn đề "abs" nhỏ nguy hiểm này sẽ trượt qua và đưa ra kết quả không chính xác. Ooh, loại điều này làm tôi khó chịu! Dù sao, cảm ơn đã chỉ ra nguyên nhân. – Mike

+2

@Mike: khi bạn nói, "loại điều này làm tôi khó chịu", bạn có nghĩa là nó khiến bạn lờ đi cảnh báo dẫn đến mã lỗi, đúng không? ;-p –

+0

Vâng, tôi đoán cả hai đều làm tôi mê mẩn. Nhưng bản chất con người là những gì nó được, tôi cố gắng thiết kế cho idiot-proofness. Những thứ như sự bao gồm 'abs' yên tĩnh này là ngược lại: chúng là những cạm bẫy ngốc nghếch. Họ rất khéo léo giấu rằng tên ngốc thậm chí không bao giờ biết anh ta/cô ấy đã bị mắc kẹt. Và để đầu nó đi, điều này một cách tích cực ngăn cản tôi từ idiot-proofing! Vì vậy, điều này irks tôi nhiều hơn nữa. – Mike

4

Trong C, bao gồm một tiêu đề chuẩn là không được phép hành động giống như nó bao gồm bất kỳ tiêu đề chuẩn nào khác. Điều này tránh được vấn đề bạn đang thấy, với chi phí đáng kể trong việc thực hiện khó khăn.

C++ cho phép bất kỳ tiêu đề chuẩn nào bao gồm bất kỳ tiêu đề chuẩn nào khác. Điều này làm cho việc triển khai thực hiện dễ dàng hơn, nhưng có thể dẫn đến chính xác loại vấn đề bạn thấy, trong đó bao gồm tiêu đề dường như không liên quan đã làm cho chức năng hiển thị mà bạn thực sự không muốn sử dụng, thay vì gặp lỗi vì hàm bạn đã sử dụng không được tuyên bố chút nào.

Thật không may, tôi không nghĩ có cách dễ dàng để giải quyết vấn đề này. Mặc dù nó khá dễ dàng để tưởng tượng <iostream> độc lập với <stdlib.h>, bạn sẽ dễ dàng hơn để xem nó có thể cần/muốn định nghĩa về những thứ như ios_base. Nó sẽ mất khá nhiều công việc phụ để xác định những thứ để cấm trước đây trong khi cho phép sau này.

Tôi nên đề cập đến, tuy nhiên, theo thời gian, tình trạng này dường như cải thiện khá nhiều. Mười năm trước, nó đã được khá phổ biến để có được hầu như tất cả các tiêu đề tiêu chuẩn từ bao gồm hầu như bất kỳ một trong số họ. Mặc dù hầu hết vẫn bao gồm ít nhất một số ít không được yêu cầu nghiêm ngặt, chúng thường gần gũi hơn với mỗi xác định chỉ những gì nó được yêu cầu.

+1

Nếu không có gì khác, bạn có thể hy vọng rằng 'iostream' bao gồm' cstdlib' thay cho 'stdlib.h', và' cstdlib' đó không bao gồm 'stdlib.h', hoặc khéo léo làm như vậy bên trong không gian tên' std'. Nhưng như bạn nói, đó là không được bảo đảm, và rõ ràng nó không phải là những gì GCC 4.2 nào. –

+1

@SteveJessop: Bạn có thể hy vọng, nhưng sau đó rất nhiều trình biên dịch gây ô nhiễm không gian tên chung khi bạn đưa vào biến thể '', trong C++ 11, hạn chế chống lại việc làm như vậy đã bị xóa ... –

1

Nếu đây là vấn đề bảo trì liên tục, tại sao không thêm một mã nhỏ vào đầu chương trình kiểm tra cụ thể cho sự cố abs()?

// test if abs() is defined incorrectly, as would happen if <stdlib.h> were 
// included by <iostream>. abs() it should return a float/double, not an int 
// (put suggestions here how to fix problem) 
if (abs(-5.1) == 5) 
{ 
    std::cerr << "Invalid build: abs() defined improperly, ..." << std::endl; 
    return 2; // exit program by returning from main 
} 

Điều đó sẽ khiến việc bỏ qua cảnh báo khó hơn rất nhiều.

+0

Ý tưởng thú vị. Nó khá xấu xí, nhưng tôi nghĩ tôi thực sự sẽ làm điều đó - ngoại trừ phần lớn mã của tôi được sử dụng như một thư viện. Vì vậy, nó không rõ ràng nơi tôi muốn đặt một thử nghiệm như vậy và làm cho nó thực sự thực hiện - ít nhất, không phải mà không làm cho giải pháp này đến nay xấu xí hơn. – Mike

+0

@Mike: Nơi hiển nhiên sẽ là bất kỳ mã nào mà luôn được gọi, như là khởi tạo thư viện. Cơ sở mã có sử dụng 'ASSERT' không? Và/hoặc có một chế độ 'DEBUG'? Bất kỳ cái nào trong số này đều tương thích với một thử nghiệm như vậy. – wallyk

+0

Không, không có nơi như vậy. Đó là một thư viện rất mô-đun và không cần khởi tạo. Điều tồi tệ hơn là tính chính xác của một 'abs' cụ thể phụ thuộc vào các câu lệnh có hay không như' using namespace std; 'được sử dụng. Trong trường hợp đó, thử nghiệm này có lẽ sẽ xảy ra trong mỗi đơn vị biên dịch - bao gồm cả người dùng, nếu có. – Mike

Các vấn đề liên quan