2012-04-12 42 views
6

Tôi đã được chơi xung quanh với C++ 11 thời gian gần đây, và đã đưa ra các chức năng tổng hợp như sau:Trình biên dịch không cảnh báo về mất chính xác?

template <typename T> 
inline T sum(const std::function<T (int)> &f, int initial, int end) 
{ 
    T retval = 0; 
    for(int k = initial; k <= end; k++) { 
     retval += f(k); 
    } 
    return retval; 
} 

Ý tưởng là tôi có thể vượt qua một hàm lambda và do đó có một chức năng gọn gàng và dễ đọc cho toán học tiền. sau đó tôi thử như sau:

int main() 
{ 
    std::array<double, 2> arr1 = {{ 0.5, 1.5 }}, 
          arr2 = {{ 1.5, -0.5 }}; 
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss! 
         0, 1); 
    return 0; 
} 

tôi biên soạn này sử dụng g ++ 4.6.3: g++ -Wall -pedantic -std=c++0x -o test main.cpp và không đưa ra bất cứ lời cảnh báo về sự mất mát chính xác tôi nhận xét trong các bình luận của mã nguồn.

Có khá nhiều điều được nêu ở đây rằng sum<int> là một điều xấu phải làm, nhưng nó có thể không rõ ràng trong các bối cảnh phức tạp hơn. Nếu trình biên dịch không nhận thấy giá trị trả về của hàm lambda của tôi là double và cảnh báo tôi rằng tôi đang mất đi độ chính xác khi truyền tới int? Có lý do cụ thể nào không?

+1

Chức năng sẽ không nhận thức được độ chính xác do thời gian chạy thực hiện một phép ẩn ngầm trước khi truyền trong các đối số. Mặc dù vậy, trong một thế giới lý tưởng, ít nhất bạn cũng nên xem cảnh báo trong studio trực quan/IDE của bạn để phản ánh rằng điều này sẽ dẫn đến mất chính xác. nhưng đây không phải là loại điều kiểm tra đơn vị nào? – War

+0

@Wardy trong một thế giới lý tưởng nó sẽ không biên dịch (tức là nó sẽ là một lỗi, không phải là một cảnh báo) mà không có một yêu cầu rõ ràng để gây ra sự mất mát chính xác này. –

+0

ok ... cũng im một nhà phát triển C# và tôi phải đi theo các quy tắc của .net (không chắc chắn nếu bạn cũng đang sử dụng một trình biên dịch .net nhưng) nó tiểu bang trong tài liệu mà một loại ít chính xác dự kiến ​​là một tiềm ẩn cast được áp dụng ... nó được coi là một tính năng của ngôn ngữ. Tôi đồng ý mặc dù ... một lỗi sẽ là giấc mơ tuyệt đối trong kịch bản của bạn nhưng không phổ biến cho nền tảng trong kinh nghiệm của tôi. – War

Trả lời

5

Có vẻ hoàn toàn hợp lý. Bạn đang yêu cầu trình biên dịch không làm phiền với Khấu trừ đối số mẫu (ví dụ: sử dụng sum<double>) nhưng thay vào đó hãy nói rõ ràng để sử dụng sum<int>. Đó là tốt như một diễn viên rõ ràng.

[Chỉnh sửa] Thế còn

template <typename F> 
auto sum(F const& f, int initial, int end) -> decltype(f(initial)) 
{ 
    auto retval = f(initial++); 
    while(initial <= end) { 
     retval += f(initial++); 
    } 
    return retval; 
} 
+0

Thật kỳ lạ nếu cảnh báo liên quan đến câu lệnh trong hàm _implementation_ phụ thuộc vào _way_ trong đó đối số mẫu đã được suy ra – user396672

+0

@ user396672: Bạn đang đề cập đến "triển khai chức năng" nào? Không có tuyên bố trong 'tổng hợp (const std :: function & f, ...)' đảm bảo cảnh báo. – MSalters

+0

return (implicit cast to int) arr1 [k] * arr2 [k]; – user396672

2

VS11 không đưa ra cảnh báo:

Warning C4189 1 cảnh báo: 'n': biến cục bộ được khởi tạo nhưng không được tham chiếu
Warning C4244 2 cảnh báo: '+ =': chuyển đổi từ 'double' thành 'int', có thể mất dữ liệu

Chỉnh sửa, thực tế cảnh báo đó là sử dụng mã:

template <typename T,typename Func> 
inline T sum(Func f, int initial, int end) 

Bạn nhận được cảnh báo khác về chuyển đổi xấu nếu bạn sử dụng std::function<T (int)>, vì vậy VS vẫn tốt về vấn đề này. (IMO, thông thường bạn nên coi các functors như là một loại templated chứ không phải là std :: function)

Ngay cả clang với -Weverything không đưa ra cảnh báo về điều này (Edit: mặc dù tôi không thể kiểm tra std :: function phiên bản với máy ATM clang). Có vẻ như thứ gì đó có thể được cải thiện.

tôi nhận được cảnh báo lạ này mặc dù:

ConsoleApplication1.cpp:15:51: warning: will never be executed [-Wunreachable-code] 
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss! 
                ^~~~ 
1

Nếu bạn Hủy các functor và nếu bạn thay đổi dòng retval += f(k); để retval += T { f(k) }; theo cách sau:

// Machinery to allow caller to indifferently use 
// sum(f, i, j) and sum<R>(f, i, j) 
struct deduced {}; 

template< 
    typename Request = deduced 
    , typename Functor 
    , typename Ret = typename std::conditional< 
     std::is_same<Request, deduced>::value 
     , typename std::result_of<Functor&(int)>::type 
     , Request 
    >::type 
> 
inline Ret sum(Functor f, int initial, int end) 
{ 
    Ret retval = 0; 
    for(int k = initial; k <= end; k++) { 
     retval += Ret { f(k) }; 
    } 
    return retval; 
} 

sau đó thay vì dựa vào sự sẵn sàng cảnh báo của trình biên dịch, bạn làm cho nó yêu cầu để phát ra chẩn đoán dưới dạng chuyển đổi thu hẹp không được phép trong khởi tạo danh sách (tức là khởi tạo với dấu ngoặc ôm).

Tôi không nghĩ rằng có một cách đáng tin cậy nếu bạn hạn chế các functor đến một std::function<Sig>. Điều đó phụ thuộc hoàn toàn vào cách triển khai đã viết std::function, khi nó phát hành cảnh báo về thu hẹp chuyển đổi và thậm chí là cảnh báo cho mã riêng của nó.

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