2017-10-17 17 views
30

Hãy xem xét ví dụ này, từ bug 80985:Xử gcc của

template <class Func> 
void call(Func f) 
{ 
    f(); 
} 

void func() noexcept { } 

int main() 
{ 
    call(func); 
} 

Biên dịch này với tất cả các cảnh báo được kích hoạt, như bạn làm, sản lượng:

$ g++ -std=c++14 -Wall foo.cxx 
foo.cxx:2:6: warning: mangled name for ‘void call(Func) [with Func = void (*)() noexcept]’ will change in C++17 because the exception specification is part of a function type [-Wnoexcept-type] 
void call(Func f) 
     ^~~~ 

gì chính xác tôi phải làm với cảnh báo này? Sửa chữa là gì?

+1

Nếu 'cuộc gọi' hoàn toàn nằm trong dự án của bạn, điều đó không quan trọng. Nó chỉ quan trọng trong trường hợp hai đơn vị dịch thuật khác nhau sử dụng nó, trong đó một đơn vị dịch được biên dịch với C++ 17 và một đơn vị không được dịch. Kể từ đó, vì 'call' là một hàm mẫu, nó có thể sẽ không có tác động lớn như vậy ngoài việc có định nghĩa phụ trong tệp thực thi cuối cùng. –

+2

@ DanielH Không phải là tôi muốn nói chuyện với Barry, ở trên, nhưng nếu bạn đang biên soạn một dự án với -wError, thì "cảnh báo vô hại" đó sẽ khiến chương trình không biên dịch, mặc dù là chính xác. Điều đó quan trọng. – markt1964

+1

@ markt1964 Trong bài viết chỉ '-Wall' được sử dụng. Nếu bạn biên dịch với '-Werror' hoặc cố gắng tránh lỗi trình biên dịch (đó là một ý tưởng hay), thì có, bạn sẽ có một vấn đề. Một trong đó có thể, có lẽ, tốt nhất được giải quyết bằng cách thêm '-Wno-noexcept-type', tùy thuộc vào hoàn cảnh. –

Trả lời

18

Có một số điều bạn có thể làm về thông báo cảnh báo.

Tắt tính năng này với -Wno-noexcept-type. Trong nhiều dự án, thông báo cảnh báo là vô ích vì không có cơ hội đối tượng kết quả sẽ được liên kết với một đối tượng khác mà hy vọng nó sẽ sử dụng tên mang tên C++ 17 của GCC. Nếu bạn không biên dịch với các cài đặt khác nhau -std= và bạn không xây dựng một thư viện tĩnh hoặc được chia sẻ trong đó chức năng vi phạm là một phần của giao diện công khai thì thông báo cảnh báo có thể bị vô hiệu hóa một cách an toàn.

Biên dịch tất cả mã của bạn bằng -std=c++17. Thông báo cảnh báo sẽ biến mất khi chức năng sẽ sử dụng tên bị xáo trộn mới.

Tạo chức năng static. Vì hàm này không còn có thể được tham chiếu bởi một tệp đối tượng khác bằng cách sử dụng một hàm mangling khác cho hàm, thông báo cảnh báo sẽ không được hiển thị. Định nghĩa hàm sẽ phải được bao gồm trong tất cả các đơn vị biên dịch sử dụng nó, nhưng đối với các hàm mẫu như trong ví dụ của bạn, đây là các anyways chung. Ngoài ra điều này sẽ không làm việc cho các chức năng thành viên là static có nghĩa là một cái gì đó khác.

Khi gọi một mẫu chức năng xác định tham số mẫu cho rõ ràng loại con trỏ chức năng tương thích không có đặc tả ngoại lệ. Ví dụ: call<void (*)()>(func). Bạn cũng có thể sử dụng diễn viên để làm điều này là tốt, nhưng GCC 7.2.0 vẫn tạo ra một cảnh báo mặc dù sử dụng -std=c++17 không thay đổi xoài.

Khi hàm không phải là mẫu không sử dụng noexcept với bất kỳ loại con trỏ hàm nào được sử dụng trong loại hàm. Điều này và điểm cuối cùng dựa trên thực tế là chỉ các kiểu con trỏ hàm không ném kết quả trong việc đặt tên các thay đổi mangling và các con trỏ hàm không ném có thể được gán (C++ 11) hoặc được chuyển đổi ngầm (C++ 17) để có thể ném con trỏ hàm.

0

Vấn đề họ đang cảnh báo bạn về là trong C++ 14, điều này sẽ làm việc:

void call(void (*f)()) 
{ 
    f(); 
} 

void func() noexcept {} 

int main(int argc, char* argv[]) 
{ 
    call(&func); 
    return 0; 
} 

nhưng trong C++ 17, bạn sẽ cần phải thay đổi tuyên bố call là:

void call(void (*f)() noexcept) 
{ 
    f(); 
} 

Vì bạn đã xác định call làm mẫu, bạn không cần phải lo lắng về điều này. Tuy nhiên, nó có thể gây ra vấn đề cho bạn bởi vì loại suy đoán đang thay đổi, điều này thường không xảy ra.

Ví dụ, mã này sẽ biên dịch trong C++ 14 nhưng không phải C++ 17:

void foo() noexcept {} 
void bar()   {} 

template <typename F> 
void call(bool b, F f1, F f2) 
{ 
    if (b) 
     f1(); 
    else 
     f2(); 
} 

void foobar(bool b) 
{ 
    call(b, &foo, &bar); 
} 

Trong C++ 14, các loại foobar đều giống nhau, nhưng chúng khác nhau trong C++ 17, có nghĩa là độ phân giải mẫu sẽ thất bại. Các thông báo lỗi trong gcc 7.2 với cờ -std=c++1z là:

note: template argument deduction/substitution failed: 
note: deduced conflicting types for parameter 'F' ('void (*)() noexcept' and 'void (*)()') 

Trong ví dụ bạn đã đưa ra, không có vấn đề và bạn sẽ không có một vấn đề biên dịch trong C++ 14 hoặc C++ 17 chế độ. Nếu mã phức tạp hơn ví dụ ở đây (ví dụ tương tự như các ví dụ tôi đã đưa ra ở trên), bạn có thể gặp phải một số vấn đề về trình biên dịch. Dường như bạn có một trình biên dịch gần đây; thử biên dịch với -std=c++1z và xem có cảnh báo hoặc lỗi hay không.

+0

Điều này không trả lời được câu hỏi. Tôi biết việc bổ sung 'noexcept' vào hệ thống kiểu. – Barry

+0

Tôi đã giải thích thêm ở phần cuối; hy vọng làm rõ vấn đề – SJL

3

Tôi đang upvoting câu trả lời của Ross cho giải pháp call<void (*)()>(func). Nó trình bày rõ ràng trình biên dịch mà bạn muốn khuôn mẫu được khởi tạo cho một loại hàm không noexcept và đảm bảo rằng mã của bạn sẽ hoạt động chính xác giống như trong C++ 17 giống như trong C++ 14.

Nhiều lựa chọn thay thế là:

(1) Gói noexcept chức năng trong một lambda (mà không phải là noexcept):

template <class Func> 
void call(Func f) 
{ 
    f(); 
} 

void func() noexcept { } 

int main() 
{ 
    call([]() { func(); }); 
} 

(2) Tạo một chức năng bao bọc riêng biệt mà không noexcept. Đây là cách nhập ban đầu nhiều hơn, nhưng nếu bạn có nhiều trang gọi, nó có thể lưu gõ tổng thể. Đây là what I ended up doing trong mã ban đầu đã nhắc tôi gửi tệp lỗi GCC.

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