2015-06-10 21 views
5

Với người dùng định nghĩa loại sau S với một chức năng chuyển đổi sang đôi:Có hợp lệ để chuyển các loại không phải là số học làm đối số cho hàm cmath không?

struct S 
{ 
    operator double() { return 1.0;} 
}; 

và các cuộc gọi sau đây để cmath chức năng sử dụng các loại S:

#include <cmath> 

void test(S s) { 
    std::sqrt(s); 
    std::log(s); 
    std::isgreater(s,1.0); 
    std::isless(s,1.0); 
    std::isfinite(s) ; 
} 

Mã này biên dịch với gcc sử dụng libstdc++ (see it live) nhưng với clang sử dụng libc++ nó tạo ra lỗi cho một số các cuộc gọi (see it live) với các lỗi sau cho isgreater:

error: no matching function for call to 'isgreater' 
    std::isgreater(s,1.0); 
    ^~~~~~~~~~~~~~ 

note: candidate template ignored: disabled by 'enable_if' [with _A1 = S, _A2 = double] 
std::is_arithmetic<_A1>::value && 
^ 

và các lỗi tương tự cho islessisfinite, vì vậy libc++ hy vọng các đối số cho những cuộc gọi đến được arithmetic typesS là không, chúng tôi có thể xác nhận điều này bằng cách truy cập nguồn cho libc++ cmath header. Mặc dù, yêu cầu đối với các loại số học không nhất quán trên tất cả các chức năng cmath trong libc++.

Vì vậy, câu hỏi đặt ra là, có hợp lệ để chuyển các loại không phải là số học làm đối số cho các hàm cmath không?

Trả lời

7

TL; DR

Theo tiêu chuẩn đó là hợp lệ để vượt qua các loại phi số học như các đối số để cmath chức năng nhưng khiếm khuyết báo cáo 2068 lập luận mục đích ban đầu là cmath chức năng nên được hạn chế số học loại và nó xuất hiện có thể bằng cách sử dụng các đối số phi số học cuối cùng sẽ được tạo thành không đúng. Vì vậy, mặc dù về mặt kỹ thuật hợp lệ bằng cách sử dụng các loại không phải số học như các đối số dường như có vấn đề trong ánh sáng của báo cáo lỗi 2068.

Chi tiết

Tiêu đề cmath được bao phủ trong dự thảo tiêu chuẩn phần 26.8[c.math] cung cấp thêm phaolong double quá tải cho mỗi chức năng quy định tại math.h mà mất một đối số đôi và hơn nữa, đoạn 11 cung cấp cho quá tải đủ và nói:

Hơn nữa, có phải thêm quá tải đủ để đảm bảo:

  1. Nếu bất kỳ đối số tương ứng với một tham số đôi đã gõ dài gấp đôi, sau đó tất cả các đối số tương ứng với các thông số đôi được hiệu quả đúc dài gấp đôi.
  2. Nếu không, nếu bất kỳ đối số nào tương ứng với tham số kép có loại gấp đôi hoặc kiểu số nguyên thì tất cả các đối số tương ứng với các tham số đôi sẽ được nhân đôi thành công.
  3. Nếu không, tất cả các đối số tương ứng với tham số kép sẽ được cast thành float.

Điều này có vẻ hợp lệ trong C++ 11

Trong phần 11 C++ 26.8[c.math] không bao gồm bất kỳ hạn chế không cho phép lập luận phi số học để cmath chức năng. Trong mỗi trường hợp từ câu hỏi, chúng tôi có quá tải sẵn có đôi đối số và những đối số này phải được chọn qua overload resolution.

báo cáo Defect 2086

Nhưng đối với C++ 14 chúng tôi có defect report 2086: Overly generic type support for math functions, mà lập luận rằng mục đích ban đầu của phần 26.8[c.math] là để hạn chế cmath chức năng chỉ có giá trị cho loại số học, mà sẽ bắt chước cách họ làm việc trong C:

Ấn tượng của tôi được rằng bộ quy tắc này có lẽ là hơn g eneric là dự định, giả định của tôi là nó được viết để bắt chước quy tắc C99/C1x được đặt trong 7,25 p2 + 3 theo cách "C++" [...] (lưu ý rằng ràng buộc C bộ hợp lệ thành các loại C++ mô tả các loại như số học, nhưng thấy dưới đây cho một sự khác biệt quan trọng) [...]

và nói:

đề nghị hiện tại của tôi để sửa chữa những vấn đề này sẽ được hạn chế loại đối số hợp lệ của các hàm này cho các kiểu số học.

và reworded phần 26.8 đoạn 11 nói (tôi nhấn mạnh):

Hơn nữa, có phải quá tải thêm đủ để đảm bảo:

  1. Nếu bất kỳ số học luận tương ứng với tham số kép có loại dài gấp đôi, sau đó tất cả số học đối số tương ứng với thông số kép được truyền thành công gấp đôi.
  2. Nếu không, nếu có tranh cãi số học tương ứng với một tham số đôi có kiểu double hoặc một loại số nguyên, sau đó tất cả số học đối số tương ứng với các thông số đôi được đúc một cách hiệu quả để đôi.
  3. Nếu không, tất cả số học đối số tương ứng với các thông số đôi được hiệu quả đúc để được đúc một cách hiệu quả để có loại phao.

Vậy điều này không hợp lệ trong C++ 14?

Vâng, mặc dù mục đích có vẻ mặt kỹ thuật vẫn còn có giá trị như lập luận trong nhận xét này khỏi các cuộc thảo luận trong libc++ bug report: incorrect implementation of isnan and similar functions:

Đó có thể là mục đích, nhưng tôi không thấy cách nào để đọc từ ngữ của tiêu chuẩn theo cách đó. Từ ví dụ ở comment # 0:

std::isnan(A()); 

Không có tham số kiểu số học, vì vậy không ai trong số những viên đạn trong 26,8/11 áp dụng. Tập quá tải chứa 'isnan (float)', 'isnan (double)' và 'isnan (long double)' và 'isnan (float)' nên được chọn.

Vì vậy, rewording bởi DR 2086 khoản không làm cho nó vô hình thành để gọi phao, đôilong double quá tải sẵn khác với lập luận phi số học.

Về mặt kỹ thuật hợp lệ nhưng vấn đề sử dụng

Vì vậy, mặc dù tiêu chuẩn C++ 11 và C++ 14 không hạn chế cmath chức năng số học lập luận DR 2068 lập luận mục đích của 26.8 đoạn 11 là để hạn chế cmath chức năng chỉ lấy các đối số số học và dường như có ý định đóng lỗ hổng trong C++ 14, nhưng không cung cấp các hạn chế đủ mạnh.

Có vẻ như đáng ngờ phải dựa vào một tính năng có thể bị ảnh hưởng xấu trong phiên bản tương lai của tiêu chuẩn. Vì chúng tôi có phân kỳ thực hiện bất kỳ mã nào dựa vào việc chuyển các đối số không phải số học đến các hàm cmath cho các trường hợp đó là không thể di chuyển và do đó sẽ chỉ hữu ích trong các trường hợp hạn chế. Chúng tôi có một giải pháp thay thế, mà là để đúc một cách rõ ràng các loại phi số học số học loại, mà bỏ qua toàn bộ vấn đề, chúng tôi không còn phải lo lắng về các mã trở thành vô hình thành và nó là xách tay:

std::isgreater(static_cast<double>(s) ,1.0) 
       ^^^^^^^^^^^^^^^^^^^^^^ 

như Potatoswatter chỉ ra sử dụng unary + cũng là một lựa chọn:

std::isgreater(+s ,1.0) 

cập nhật

như TC chỉ ra trong C++ 11 nó có thể lập luận rằng 26.8 đoạn 11 đạn 3 áp dụng kể từ khi tranh luận không phải là long double, đôi hay một số nguyên và nên do đó các tham số kiểu S nên được đúc để phao đầu tiên . Lưu ý, như được chỉ ra bởi báo cáo lỗi gcc không bao giờ thực hiện điều này và theo như tôi biết không, cũng không clang.

+0

Có thể cho rằng, từ ngữ C++ 11 yêu cầu 's' được đưa vào' float', vì loại của nó không phải là 'dài gấp đôi' cũng không phải' double' cũng không phải là một kiểu số nguyên. –

+0

@ T.C. Tôi tiếp tục tranh luận về điều đó, nhưng có, điều đó là đáng tranh cãi. Lưu ý, không phải 'libC++' cũng không 'libstdC++' thực hiện nó theo cách đó. –

+0

'+ s' có thể được xem là thành ngữ. – Potatoswatter

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