Tại sao các thông báo lỗi mẫu C ++ rất khủng khiếp?


28

Các mẫu C ++ nổi tiếng với việc tạo các thông báo lỗi dài, không thể đọc được. Tôi có một ý tưởng chung về lý do tại sao các thông báo lỗi mẫu trong C ++ rất tệ. Về cơ bản, vấn đề là lỗi không được kích hoạt cho đến khi trình biên dịch gặp cú pháp không được hỗ trợ bởi một loại nhất định trong mẫu. Ví dụ:

template <class T>
void dosomething(T& x) { x += 5; }

Nếu Tkhông hỗ trợ +=toán tử, trình biên dịch sẽ tạo thông báo lỗi. Và nếu điều này xảy ra sâu trong một thư viện ở đâu đó, thông báo lỗi có thể dài hàng ngàn dòng.

Nhưng các mẫu C ++ về cơ bản chỉ là một cơ chế để gõ vịt thời gian biên dịch. Một lỗi mẫu C ++ về mặt khái niệm rất giống với lỗi loại thời gian chạy có thể xảy ra trong một ngôn ngữ động như Python. Ví dụ, hãy xem xét mã Python sau:

def dosomething(x):
   x.foo()

Ở đây, nếu xkhông có foo()phương thức, trình thông dịch Python sẽ đưa ra một ngoại lệ và hiển thị dấu vết ngăn xếp cùng với thông báo lỗi khá rõ ràng cho biết sự cố. Ngay cả khi lỗi không được kích hoạt cho đến khi trình thông dịch nằm sâu bên trong một số chức năng của thư viện, thông báo lỗi thời gian chạy vẫn không ở đâu tệ như tiếng nôn không thể đọc được của trình biên dịch C ++ thông thường. Vậy tại sao trình biên dịch C ++ không thể rõ hơn về những gì đã sai? Tại sao một số thông báo lỗi mẫu C ++ theo nghĩa đen khiến cửa sổ bảng điều khiển của tôi cuộn trong hơn 5 giây?


6
Một số trình biên dịch có thông báo lỗi khủng khiếp, nhưng những trình biên dịch khác thực sự tốt ( clang++nháy mắt).
Benjamin Bannier

2
Vì vậy, bạn muốn các chương trình của bạn thất bại trong thời gian chạy, vận chuyển, trong tay của một khách hàng, thay vì thất bại tại thời gian biên dịch?
P Shved

13
@Pavel, không. Câu hỏi này không phải là về những lợi thế / bất lợi của thời gian chạy so với kiểm tra lỗi thời gian biên dịch.
Kênh72

1
Như một ví dụ về lỗi mẫu C ++ lớn, FWIW: codegolf.stackexchange.com/a/10470/7174
kebs 22/03/18

Câu trả lời:


28

Thông báo lỗi mẫu có thể nổi tiếng, nhưng không phải lúc nào cũng dài và không thể đọc được. Trong trường hợp này, toàn bộ thông báo lỗi (từ gcc) là:

test.cpp: In function void dosomething(T&) [with T = X]’:
test.cpp:11:   instantiated from here
test.cpp:6: error: no match for operator+=’ in x += 5

Như trong ví dụ Python của bạn, bạn nhận được "dấu vết ngăn xếp" của các điểm khởi tạo mẫu và thông báo lỗi rõ ràng cho biết sự cố.

Đôi khi, các thông báo lỗi liên quan đến mẫu có thể lâu hơn, vì nhiều lý do:

  • "Dấu vết ngăn xếp" có thể sâu hơn nhiều
  • Các tên loại có thể dài hơn nhiều, vì các mẫu được khởi tạo với các khởi tạo mẫu khác làm đối số của chúng và được hiển thị với tất cả các vòng loại không gian tên của chúng
  • Khi giải quyết quá tải không thành công, thông báo lỗi có thể chứa danh sách quá tải ứng viên (mỗi thông báo có thể chứa một số tên loại rất dài)
  • Lỗi tương tự có thể được báo cáo nhiều lần, nếu một mẫu không hợp lệ được khởi tạo ở nhiều nơi

Sự khác biệt chính từ Python là hệ thống kiểu tĩnh, dẫn đến sự cần thiết phải bao gồm tên loại (đôi khi dài) trong thông báo lỗi. Không có chúng, đôi khi sẽ rất khó để chẩn đoán tại sao độ phân giải quá tải không thành công. Với họ, thách thức của bạn không còn là đoán được vấn đề ở đâu, mà là giải mã các chữ tượng hình cho bạn biết nó ở đâu.

Ngoài ra, kiểm tra trong thời gian chạy có nghĩa là chương trình sẽ dừng ở lỗi đầu tiên mà nó gặp phải, chỉ hiển thị một thông báo duy nhất. Một trình biên dịch có thể hiển thị tất cả các lỗi mà nó gặp phải, cho đến khi nó bỏ cuộc; ít nhất là trong C ++, nó không nên dừng ở lỗi đầu tiên trong tệp, vì đó có thể là hậu quả của lỗi sau này.


4
Bạn có thể cho một ví dụ về một lỗi là hậu quả của một lỗi sau này?
Ruslan

12

Một số lý do rõ ràng bao gồm:

  1. Lịch sử. Khi gcc, MSVC, v.v., mới, họ không đủ khả năng sử dụng nhiều không gian để lưu trữ dữ liệu để tạo thông báo lỗi tốt hơn. Bộ nhớ đã khan hiếm đến mức họ không thể.
  2. Trong nhiều năm, người tiêu dùng đã bỏ qua chất lượng thông báo lỗi, vì vậy các nhà cung cấp chủ yếu đã làm quá.
  3. Với một số mã, trình biên dịch có thể đồng bộ hóa lại và chẩn đoán các lỗi thực sự sau này trong mã. Lỗi trong các tầng xếp tầng rất tệ đến nỗi mọi thứ trước đây hầu như luôn vô dụng.
  4. Sự linh hoạt chung của các mẫu làm cho nó khó có thể đoán những gì bạn có thể có nghĩa là khi mã của bạn có lỗi.
  5. Bên trong một mẫu, ý nghĩa của một tên phụ thuộc vào cả bối cảnh của mẫu và bối cảnh của việc khởi tạo tra cứu phụ thuộc đối số có thể vẫn còn nhiều khả năng hơn.
  6. Quá tải chức năng có thể cung cấp rất nhiều ứng cử viên cho những gì mà một chức năng gọi cụ thể có thể đề cập đến, và một số trình biên dịch (ví dụ: gcc) liệt kê tất cả chúng khi có sự mơ hồ.
  7. Nhiều lập trình viên không bao giờ xem xét việc sử dụng các tham số bình thường mà không đảm bảo rằng các giá trị được thông qua đáp ứng yêu cầu, thậm chí không cố gắng kiểm tra các tham số mẫu (và tôi phải thú nhận, tôi có xu hướng về điều này).

Đó là xa đầy đủ, nhưng bạn có được ý tưởng chung. Ngay cả khi nó không dễ dàng, hầu hết đều có thể được chữa khỏi. Trong nhiều năm, tôi đã nói với mọi người để có được một bản sao của Comeau C ++ để sử dụng thường xuyên; Có lẽ tôi đã lưu đủ từ một thông báo lỗi một lần để trả tiền cho trình biên dịch. Bây giờ Clang đang đi đến cùng một điểm (và nó thậm chí còn rẻ hơn).

Tôi sẽ kết thúc bằng một quan sát chung nghe có vẻ như một trò đùa, nhưng thực sự không phải vậy. Hầu hết thời gian, công việc thực sự của một nhà soạn nhạc biến mã nguồn thành các thông báo lỗi. Đã đến lúc các nhà cung cấp tập trung vào làm công việc đó tốt hơn một chút - mặc dù tôi công khai thừa nhận rằng khi tôi viết trình biên dịch, tôi có xu hướng mạnh mẽ coi nó là thứ yếu (tốt nhất) và trong một số trường hợp gần như bỏ qua nó hoàn toàn.


9

Câu trả lời đơn giản là bởi vì Python được thiết kế để hoạt động theo cách đó, trong khi rất nhiều thứ liên quan đến các mẫu xuất hiện một cách tình cờ. Chẳng hạn, nó không bao giờ có ý định trở thành một hệ thống hoàn chỉnh Turing. Và nếu bạn không thể cố tình lập kế hoạch và lý do về những gì xảy ra khi hệ thống của bạn hoạt động , tại sao mọi người nên mong đợi kế hoạch cẩn thận, chu đáo về những gì xảy ra khi có sự cố?

Ngoài ra, như bạn đã chỉ ra, trình thông dịch Python có thể giúp bạn dễ dàng hơn rất nhiều bằng cách hiển thị dấu vết ngăn xếp vì nó diễn giải mã Python. Nếu trình biên dịch C ++ gặp lỗi mẫu và cung cấp cho bạn dấu vết ngăn xếp, điều đó sẽ không hữu ích như "nôn mửa mẫu", phải không?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.