2012-06-18 10 views
24

Tôi đang phát triển một dự án hoạt động với nhiều loại số học. Vì vậy, tôi đã thực hiện một tiêu đề, nơi các yêu cầu tối thiểu cho một người dùng định nghĩa kiểu số học được định nghĩa:Tại sao một số chức năng trong <cmath> không có trong không gian tên std?

user_defined_arithmetic.h:

typedef double ArithmeticF; // The user chooses what type he 
           // wants to use to represent a real number 

namespace arithmetic   // and defines the functions related to that type 
{ 

const ArithmeticF sin(const ArithmeticF& x); 
const ArithmeticF cos(const ArithmeticF& x); 
const ArithmeticF tan(const ArithmeticF& x); 
... 
} 

gì đang gây phiền toái cho tôi là khi tôi sử dụng mã như thế này:

#include "user_defined_arithmetic.h" 

void some_function() 
{ 
    using namespace arithmetic; 
    ArithmeticF lala(3); 
    sin(lala); 
} 

tôi nhận được một lỗi biên dịch:

error: call of overloaded 'sin(ArithmeticF&)' is ambiguous 
candidates are: 
double sin(double) 
const ArithmeticF arithmetic::sin(const ArithmeticF&) 

Tôi chưa bao giờ sử dụng tiêu đề <math.h>, chỉ có <cmath>. Tôi chưa bao giờ sử dụng using namespace std trong tệp tiêu đề.

Tôi đang sử dụng gcc 4.6. *. Tôi đã kiểm tra các tiêu đề có chứa các tuyên bố mơ hồ là gì và nó hóa ra là:

mathcalls.h:

Prototype declarations for math functions; helper file for <math.h>. 
... 

tôi biết, rằng <cmath> bao gồm <math.h>, nhưng nó phải che chắn các tờ khai do không gian tên std. Tôi thâm nhập vào các <cmath> header và tìm thấy:

cmath.h:

... 

#include <math.h> 

... 

// Get rid of those macros defined in <math.h> in lieu of real functions. 
#undef abs 
#undef div 
#undef acos 
... 

namespace std _GLIBCXX_VISIBILITY(default) 
{ 
... 

Vì vậy, std namespace bắt đầu sau các #include <math.h>. Có điều gì sai ở đây không, hay tôi đã hiểu nhầm điều gì đó?

+2

Một số điều bạn có thể muốn xem xét lại: khi sử dụng các loại số học (không thể thiếu các loại + đôi + float) nó thường là hiệu quả hơn (và thông thường) để vượt qua giá trị so với tham chiếu. Khi gọi một hàm mà bạn muốn một phiên bản cụ thể, hãy hội đủ điều kiện cuộc gọi, thay vì thêm một 'không gian tên' X'. Hoặc bạn có thể sử dụng chỉ thị * bằng cách sử dụng * ('sử dụng số học :: sin'). Cuối cùng, toàn bộ cách tiếp cận của việc thay đổi kiểu bằng cách chỉnh sửa 'typedef' là một ý tưởng tồi. –

+0

@ DavidRodriguez-dribeas: Cảm ơn bạn! Xin vui lòng, bạn có thể gợi ý cho tôi một giải pháp thay thế? Tôi đang sử dụng đi qua tham chiếu, bởi vì số lượng có thể là một loại tùy chỉnh. Điều đó có nghĩa là nó có thể nhận được vài kilobyte lớn. Tôi đã hy vọng rằng khi tôi inline các chức năng và sử dụng các chức năng cơ bản std bên trong nội tuyến, không có hại sẽ được thực hiện. Hay sẽ là? Bạn có thể cho tôi một số gợi ý xin vui lòng? –

+0

@ DavidRodriguez-dribeas: Tôi biết rằng cách tiếp cận C++ sẽ là khai báo một lớp trừu tượng, nhưng một thư viện mà tôi sử dụng để tính toán ma trận sử dụng tối ưu hóa đáng kể khi bạn sử dụng một kiểu được xây dựng sẵn. Tôi chỉ không muốn mất lợi thế này –

Trả lời

16

Việc triển khai thư viện chuẩn C++ được phép khai báo hàm thư viện C trong không gian tên chung cũng như trong std. Một số sẽ gọi đây là một sai lầm, vì (như bạn đã tìm thấy) sự ô nhiễm không gian tên có thể gây ra xung đột với tên riêng của bạn. Tuy nhiên, đó là cách nó là, vì vậy chúng ta phải sống với nó. Bạn sẽ chỉ phải đủ điều kiện tên của mình là arithmetic::sin.

Trong những lời của các tiêu chuẩn (11 17.6.1.2/4 C++):

In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the namespace std . It is unspecified whether these names are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations (7.3.3).

3

Nếu bạn thực sự muốn, bạn luôn có thể viết một chút wrapper xung quanh cmath, dọc theo dòng:

//stdmath.cpp 
#include <cmath> 
namespace stdmath 
{ 
    double sin(double x) 
    { 
     return std::sin(x); 
    } 
} 

//stdmath.hpp 
#ifndef STDMATH_HPP 
#define STDMATH_HPP 
namespace stdmath { 
    double sin(double); 
} 
#endif 

//uses_stdmath.cpp 
#include <iostream> 
#include "stdmath.hpp" 

double sin(double x) 
{ 
    return 1.0; 
} 

int main() 
{ 
    std::cout << stdmath::sin(1) << std::endl; 
    std::cout << sin(1) << std::endl; 
} 

Tôi cho rằng có thể có một số phí từ cuộc gọi hàm bổ sung, tùy thuộc vào mức độ thông minh của trình biên dịch.

+0

Điều này không hoàn toàn giải quyết được vấn đề, hãy xem xét 'namespace mylib { đôi tội lỗi (gấp đôi x) { return 1.0; } } int main() { \t sử dụng không gian tên mylib; std :: cout << stdmath :: tội lỗi (1) << std :: endl; std :: cout << sin (1) << std :: endl; } 'bạn vẫn nhận được một" lỗi cuộc gọi mơ hồ ". – alfC

+0

@alfC không, bạn không biết. Toàn bộ điểm của câu trả lời này được đưa vào '' trong tệp 'cpp' của' stdmath' thay cho tiêu đề 'hpp'. – Ruslan

1

Đây chỉ là một nỗ lực khiêm tốn để bắt đầu giải quyết vấn đề này. (Đề nghị được hoan nghênh.)

Tôi đã giải quyết vấn đề này một thời gian dài. Một trường hợp là vấn đề rất rõ ràng là trường hợp này:

#include<cmath> 
#include<iostream> 

namespace mylib{ 
    std::string exp(double x){return "mylib::exp";} 
} 

int main(){ 
    std::cout << std::exp(1.) << std::endl; // works 
    std::cout << mylib::exp(1.) << std::endl; // works 

    using namespace mylib; 
    std::cout << exp(1.) << std::endl; //doesn't works!, "ambiguous" call 
    return 0; 
} 

Đây là ý kiến ​​của tôi là một lỗi gây phiền nhiễu hoặc ít nhất là một tình huống rất đáng tiếc.(Ít nhất là trong GCC, và kêu gọi - sử dụng thư viện GCC - trong Linux.)

Gần đây tôi đã đưa ra một cảnh quay khác cho vấn đề này. Bằng cách nhìn vào các cmath (của GCC) có vẻ như là tiêu đề là có đơn giản để quá tải các chức năng C và vít lên không gian tên trong quá trình này.

namespace std{ 
    #include<math.h> 
} 
//instead of #include<cmath> 

Với nó này hoạt động

using namespace mylib; 
std::cout << exp(1.) << std::endl; //now works. 

Tôi gần như chắc chắn rằng đây không phải là chính xác tương đương với #include<cmath> nhưng hầu hết các chức năng có vẻ làm việc.

Điều tồi tệ nhất là cuối cùng một số thư viện phụ thuộc cuối cùng sẽ là #inclulde<cmath>. Cho rằng tôi không thể tìm thấy một giải pháp nào được nêu ra.

LƯU Ý: Không cần phải nói điều này không làm việc ở tất cả các

namespace std{ 
    #include<cmath> // compile errors 
} 
+2

Khai báo bất kỳ thứ gì trong 'namespace std' là UB. – Ruslan