Các vấn đề của việc đưa C ++ - như const thành ngôn ngữ là gì?


17

Tôi quan tâm đến ý tưởng về C ++ - giống như constkhông thực hiện cụ thể đó (như bỏ đi const).

Lấy ví dụ C # - nó thiếu C ++ - như const, và lý do cho nó là thông thường - con người và thời gian. Ngoài ra, có vẻ như nhóm C # đã xem xét việc thực hiện C ++ const, tiếp thị CLR và đã có đủ tại thời điểm này (xem tại sao không có phương thức thành viên const trong tham số c # và const ; cảm ơn Svick). Nếu chúng ta di chuyển xa hơn thế, còn gì nữa không?

Có một cái gì đó sâu sắc hơn? Lấy ví dụ đa kế thừa - nó thường được xem là khó nắm bắt (đối với người dùng) và do đó không được thêm vào ngôn ngữ (như vấn đề kim cương).

Có điều gì đó trong BẢN CHẤT của constvấn đề đó đặt ra một vấn đề là tốt hơn cho ngôn ngữ để tránh nó? Một cái gì đó như quyết định constnên sâu hay nông (có constthùng chứa có nghĩa là tôi không thể thêm phần tử mới hoặc thay đổi các phần tử hiện có; nếu các phần tử thuộc loại tham chiếu) thì sao?

CẬP NHẬT : trong khi tôi đề cập đến C #, và do đó đưa ra một viễn cảnh lịch sử, tôi quan tâm đến bản chất của các vấn đề tiềm ẩn constđối với ngôn ngữ.

Đây không phải là một thách thức của các ngôn ngữ. Hãy bỏ qua các yếu tố như xu hướng hiện tại hoặc mức độ phổ biến - Tôi chỉ quan tâm đến các vấn đề kỹ thuật - cảm ơn bạn.


3
như một bên: nhiều kế thừa có thể không khó nắm bắt, nhưng độ phân giải phương pháp có thể trở nên khá xấu. Độ phân giải sâu đầu tiên nhanh chóng trở nên không trực quan ( vấn đề kim cương của Hồi giáo ). Thứ tự giải quyết phương pháp C3 (đã xuất bản '96) giải quyết điều này, nhưng chắc chắn không dễ hiểu. Do đó, việc đặt ra nhiều kế thừa có thể có vẻ khá hợp lý - nhưng vấn đề thực sự là Java có trước thuật toán C3 và chính C # đã định hướng theo Java.
amon

3
@amon Rất nhiều người không thực sự nghĩ rằng nhiều tính kế thừa là một tính năng bạn muốn có ngay từ đầu, ví dụ: những người tạo ra lập trình D đã bỏ qua: Đây là một tính năng phức tạp của giá trị gây tranh cãi. Rất khó để thực hiện một cách hiệu quả và trình biên dịch dễ gặp nhiều lỗi khi thực hiện nó. Gần như tất cả giá trị của MI có thể được xử lý với sự kế thừa duy nhất kết hợp với giao diện và tập hợp. Những gì còn lại không chứng minh trọng lượng của việc thực hiện MI. (nguồn)
jcora


2
Ngay cả khi không có Đa kế thừa, bạn đã có thể mã hóa bất kỳ vấn đề 3-SAT nào dưới dạng Độ phân giải phương thức (hay chính xác hơn là Độ phân giải quá tải) trong C #, do đó làm cho nó hoàn thành NP.
Jörg W Mittag

1
@svick, cảm ơn bạn rất nhiều. Vì tôi không có câu trả lời nào cho câu hỏi này, tôi đã tự do chỉnh sửa nó, để tập trung vào bản chất của vấn đề, chứ không phải lý do lịch sử, vì dường như 99% trong số đó là "thiên vị thời gian + người + chống C ++ + CLR ".
greenoldman

Câu trả lời:


10

Lưu ý chắc chắn nếu điều này đủ điều kiện cho bạn, nhưng trong các ngôn ngữ chức năng như Standard ML, mọi thứ đều không thay đổi theo mặc định. Đột biến được hỗ trợ thông qua một loại reference chung . Vì vậy, một intbiến là bất biến, và một ref intbiến là một thùng chứa có thể thay đổi cho ints. Về cơ bản, các biến là các biến thực theo nghĩa toán học (một giá trị không xác định nhưng cố định) và refs là "biến" theo nghĩa lập trình mệnh lệnh - một ô nhớ có thể được ghi và đọc từ đó. (Tôi thích gọi chúng là chuyển nhượng .)

Tôi nghĩ rằng vấn đề với constlà hai lần. Đầu tiên, C ++ thiếu bộ sưu tập rác, cần thiết để có cấu trúc dữ liệu liên tục không tầm thường . const phải sâu sắc để có ý nghĩa, nhưng có các giá trị bất biến hoàn toàn trong C ++ là không thực tế.

Thứ hai, trong C ++, bạn cần chọn tham gia constthay vì từ chối. Nhưng khi bạn quên constmột cái gì đó và sau đó sửa nó, bạn sẽ gặp phải tình huống "ngộ độc const" được đề cập trong câu trả lời của @ RobY, nơi constthay đổi sẽ xếp tầng trong toàn bộ mã. Nếu constlà mặc định, bạn sẽ không thấy mình áp dụng consthồi tố. Ngoài ra, việc phải thêm constở mọi nơi sẽ tạo ra nhiều tiếng ồn cho mã.

Tôi nghi ngờ các ngôn ngữ chính tiếp theo (ví dụ Java) được định hình rất nhiều bởi sự thành công và cách suy nghĩ của C và C ++. Trường hợp cụ thể, ngay cả với bộ sưu tập rác, hầu hết các API bộ sưu tập của các ngôn ngữ đều giả định cấu trúc dữ liệu có thể thay đổi. Thực tế là tất cả mọi thứ đều có thể thay đổi và bất biến được xem như một trường hợp góc nói lên rất nhiều về tư duy bắt buộc đằng sau các ngôn ngữ phổ biến.

EDIT : Sau khi phản ánh nhận xét của greenoldman, tôi nhận ra rằng điều đó constkhông trực tiếp về tính bất biến của dữ liệu; constmã hóa thành kiểu của phương thức cho dù nó có tác dụng phụ trên thể hiện.

Có thể sử dụng đột biến để đạt được hành vi minh bạch tham chiếu . Giả sử bạn có một hàm mà khi được gọi liên tiếp trả về các giá trị khác nhau - ví dụ: một hàm đọc một ký tự từ đó stdin. Chúng ta có thể sử dụng bộ đệm / ghi nhớ kết quả của hàm này để tạo ra một luồng giá trị trong suốt tham chiếu. Luồng sẽ là một danh sách được liên kết có các nút sẽ gọi hàm trong lần đầu tiên bạn cố truy xuất giá trị của chúng, nhưng sau đó lưu lại kết quả. Vì vậy, nếu là stdinhằng số Hello, world!, lần đầu tiên bạn cố gắng lấy giá trị của nút đầu tiên, nó sẽ đọc một nút charvà trả về H. Sau đó, nó sẽ tiếp tục quay trở lại Hmà không cần gọi thêm để đọc a char. Tương tự, nút thứ hai sẽ đọc chartừstdinlần đầu tiên bạn cố gắng lấy lại giá trị của nó, lần này quay lại evà lưu vào kết quả đó.

Điều thú vị ở đây là bạn đã biến một quá trình vốn đã thành trạng thái thành một đối tượng dường như không quốc tịch. Tuy nhiên, cần phải thay đổi trạng thái bên trong của đối tượng (bằng cách lưu trữ kết quả) để đạt được điều này - đột biến là một hiệu ứng lành tính . Không thể làm cho chúng tôi CharStream constmặc dù luồng hoạt động như một giá trị bất biến. Bây giờ hãy tưởng tượng có một Streamgiao diện với constcác phương thức và tất cả các chức năng của bạn đều mong đợi const Streams. Bạn CharStreamkhông thể thực hiện giao diện!

( EDIT 2: Rõ ràng có một C ++ từ khóa gọi mutablesẽ cho phép chúng tôi để lừa và làmCharStream const Tuy nhiên, lỗ hổng này phá hủy. constS đảm bảo' - bây giờ bạn thực sự không thể chắc chắn điều gì đó sẽ không đột biến thông qua của nó constphương pháp Tôi cho rằng nó không phải là. xấu vì bạn phải yêu cầu lỗ hổng một cách rõ ràng, nhưng bạn vẫn hoàn toàn phụ thuộc vào hệ thống danh dự.)

Thứ hai, giả sử bạn có các hàm bậc cao - nghĩa là bạn có thể truyền các hàm dưới dạng đối số cho các hàm khác. constNess là một phần của chữ ký của hàm, vì vậy bạn sẽ không thể chuyển các consthàm không phải là đối số cho các hàm mong đợi các consthàm. Thực thi một cách mù quáng constở đây sẽ dẫn đến mất tính tổng quát.

Cuối cùng, thao túng một constđối tượng không đảm bảo rằng nó không làm biến đổi một số trạng thái bên ngoài (tĩnh hoặc toàn cầu) phía sau lưng của bạn, vì vậy const, đảm bảo không mạnh như lúc ban đầu xuất hiện.

Tôi không rõ ràng rằng mã hóa sự hiện diện hay vắng mặt của các tác dụng phụ vào hệ thống loại là một điều tốt.


1
+1 cảm ơn bạn. Các đối tượng không thay đổi là một cái gì đó khác với C ++ const (nó là một góc nhìn nhiều hơn). Nhưng giả sử, C ++ sẽ có const theo mặc định và vartheo yêu cầu sẽ chỉ để lại một vấn đề - độ sâu?
greenoldman

1
@greenoldman Điểm tốt. Đã được một thời gian kể từ khi tôi sử dụng C ++ nên tôi quên rằng bạn có thể có một consttham chiếu đến một constđối tượng không . Mặc dù vậy, tôi không tin rằng nó có ý nghĩa gì khi nông cạn const. Toàn bộ vấn đề constlà đảm bảo rằng bạn sẽ không thể sửa đổi đối tượng thông qua tham chiếu đó. Bạn không được phép phá vỡ constbằng cách tạo một consttài liệu không tham chiếu, vậy tại sao việc phá vỡ constbằng cách thay đổi các thành viên của đối tượng là ổn?
Doval

+1 :-) Các vấn đề rất thú vị trong bản cập nhật của bạn (sống với lambdas không / const và có thể là vấn đề yếu hơn với trạng thái bên ngoài), tôi phải tiêu hóa nó lâu hơn. Dù sao, đối với nhận xét của bạn, tôi không có bất cứ điều gì xấu xa - hoàn toàn ngược lại, tôi đang tìm kiếm các tính năng để tăng sự rõ ràng của mã (một số khác: con trỏ không null). Cùng một danh mục ở đây (ít nhất đó là mục đích của tôi) - Tôi xác định func foo(const String &s)và đây là tín hiệu ssẽ không được thay đổi foo.
greenoldman

1
@greenoldman Tôi chắc chắn có thể thấy sự hấp dẫn và lý do. Tuy nhiên, tôi không nghĩ rằng có một giải pháp thực sự cho vấn đề ngoài việc tránh trạng thái có thể thay đổi càng nhiều càng tốt (cùng với những điều khó chịu khác như nullvà thừa kế.)
Doval

8

Vấn đề chính là các lập trình viên có xu hướng không sử dụng đủ, vì vậy khi họ đến một nơi cần thiết, hoặc một người bảo trì sau đó muốn sửa lỗi chính xác, có một hiệu ứng gợn rất lớn. Bạn vẫn có thể viết mã bất biến hoàn toàn tốt mà không cần const, trình biên dịch của bạn sẽ không thực thi mã đó và sẽ khó khăn hơn trong việc tối ưu hóa mã. Một số người thích trình biên dịch của họ không giúp họ. Tôi không hiểu những người đó, nhưng có rất nhiều người trong số họ.

Trong các ngôn ngữ chức năng, hầu hết mọi thứ đều có const, và có thể thay đổi là ngoại lệ hiếm, nếu nó được cho phép. Điều này có một số lợi thế, chẳng hạn như đồng thời dễ dàng hơn và lý luận dễ dàng hơn về trạng thái chia sẻ, nhưng sẽ có một số quen thuộc nếu bạn đến từ một ngôn ngữ có văn hóa đột biến. Nói cách khác, không muốn constlà vấn đề của mọi người, không phải là vấn đề kỹ thuật.


+1 cảm ơn bạn. Mặc dù đối tượng bất biến là thứ gì đó khác với tôi đang tìm kiếm (bạn có thể làm cho đối tượng bất biến trong C # hoặc Java), ngay cả với phương pháp bất biến bạn phải quyết định xem nó sâu hay nông, ví dụ như chứa các con trỏ / tham chiếu (bạn có thể thay đổi giá trị của các đối tượng được trỏ đến). Sau một ngày, có vẻ như vấn đề chính là những gì được đặt làm mặc định và có thể giải quyết dễ dàng khi thiết kế ngôn ngữ mới.
greenoldman

1
+1 cho lolz @ "Một số người thích trình biên dịch của họ không giúp họ. Tôi không hiểu những người đó, nhưng có rất nhiều người trong số họ."
Thomas Eding

6

Tôi thấy những nhược điểm như:

  • "Nhiễm độc const" là một vấn đề khi một tuyên bố của const có thể buộc một tuyên bố trước hoặc sau sử dụng const và điều đó có thể khiến các tuyên bố trước đây của nó sử dụng const, v.v. Và nếu nhiễm độc const chảy vào một lớp trong thư viện rằng bạn không có quyền kiểm soát, sau đó bạn có thể bị mắc kẹt ở một nơi tồi tệ.

  • nó không phải là một sự đảm bảo tuyệt đối Thường có những trường hợp const chỉ bảo vệ tham chiếu, nhưng không thể bảo vệ các giá trị cơ bản vì lý do này hay lý do khác. Ngay cả trong C, có những trường hợp không thể thực hiện được, điều này làm suy yếu lời hứa mà const cung cấp.

  • nói chung, tình cảm của ngành công nghiệp dường như đang tránh xa các đảm bảo thời gian biên dịch mạnh mẽ, tuy nhiên rất nhiều sự thoải mái họ có thể mang lại cho bạn và tôi. Vì vậy, vào buổi chiều, tôi dành 66% cơ sở mã của mình vì bị đầu độc, một số Python anh ấy đã tìm ra 10 mô-đun Python hoàn toàn khả thi, được thiết kế tốt, được kiểm tra tốt, đầy đủ chức năng. Và anh ta thậm chí không hack khi anh ta làm điều đó.

Vì vậy, có lẽ câu hỏi là, tại sao lại mang đến một tính năng có thể gây tranh cãi mà thậm chí một số chuyên gia còn mơ hồ khi bạn đang cố gắng áp dụng ngôn ngữ mới khó hiểu của mình?

EDIT: câu trả lời ngắn, một ngôn ngữ mới có một ngọn đồi dốc để leo lên để nhận con nuôi. Các nhà thiết kế thực sự cần phải suy nghĩ kỹ về những tính năng cần thiết để có được sự chấp nhận. Lấy đi, ví dụ. Google đã loại bỏ một cách tàn nhẫn một số trận chiến tôn giáo sâu sắc nhất bằng cách đưa ra một số lựa chọn thiết kế theo phong cách Conan-the-Barbary, và tôi thực sự nghĩ rằng ngôn ngữ này tốt hơn cho nó, từ những gì tôi đã thấy.


2
Viên đạn thứ hai của bạn không đúng. Trong C ++, có ba / bốn cách để khai báo một tham chiếu / con trỏ, wrt. constness: int *con trỏ có thể thay đổi thành giá trị có thể thay đổi, int const *con trỏ có thể thay đổi thành giá trị int * constconst , con trỏ const thành giá trị có thể thay đổi, int const * constcon trỏ const thành giá trị const.
phresnel

Cảm ơn bạn. Tôi đã hỏi về bản chất, không phải tiếp thị ("ngành công nghiệp chuyển đi" không phải là bản chất của C ++ - như vậy const), do đó, "những lý do" như vậy tôi cho là không liên quan (ít nhất là cho câu hỏi này). Về constngộ độc - bạn có thể cho ví dụ? Bởi vì AFAIK là lợi thế, bạn vượt qua một cái gì đó constvà nó vẫn còn const.
greenoldman

1
Nó không phải là constvấn đề nhưng thay đổi loại thực sự - bất cứ điều gì bạn làm, nó sẽ luôn luôn là một vấn đề. Xem xét vượt qua RichString, và sau đó nhận ra bạn vượt qua tốt hơn String.
greenoldman

4
@RobY: Tôi không chắc chắn những gì / người mà bạn đang giải quyết với sau nàytrước đây . Tuy nhiên: Sau buổi chiều đi qua mã, bạn nên biết rằng bạn nên cấu trúc lại để xác định chính xác từ dưới lên, không phải từ trên xuống, tức là bắt đầu trong các đơn vị trong cùng, và làm việc theo cách của bạn. Có thể các đơn vị của bạn cũng không đủ cách ly, thay vào đó được liên kết chặt chẽ hoặc bạn đã trở thành con mồi của các hệ thống bên trong.
phresnel

7
"Nhiễm độc Const" không thực sự là một vấn đề - vấn đề thực sự là C ++ mặc định là không const. Thật quá dễ dàng để không phải là constnhững thứ nên constbởi vì bạn phải chọn tham gia thay vì ra khỏi nó.
Doval

0

Có một số vấn đề triết học trong việc thêm constvào một ngôn ngữ. Nếu bạn khai báo constvới một lớp, điều đó có nghĩa là lớp đó không thể thay đổi. Nhưng một lớp là một tập hợp các biến được kết hợp với các phương thức (hoặc bộ biến đổi). Chúng ta đạt được sự trừu tượng bằng cách ẩn trạng thái của một lớp đằng sau một tập các phương thức. Nếu một lớp được thiết kế để ẩn trạng thái, thì bạn cần các trình biến đổi để thay đổi trạng thái đó từ bên ngoài và sau đó nó không constcòn nữa. Vì vậy, chỉ có thể khai báo constcho các lớp là bất biến. Vì vậy, constdường như chỉ có thể trong các tình huống trong đó các lớp (hoặc các loại bạn muốn khai báo const) là tầm thường ...

Vậy chúng ta có cần constkhông? Tôi không nghĩ vậy. Tôi nghĩ rằng bạn có thể dễ dàng làm mà không cần constnếu bạn có một thiết kế tốt. Bạn cần dữ liệu nào và ở đâu, phương thức nào là phương thức dữ liệu và dữ liệu trừu tượng nào bạn cần cho I / O, mạng, xem, v.v. Sau đó, bạn tự nhiên phân tách các mô-đun và lớp phát sinh liên quan đến trạng thái và bạn có phương thức và các lớp bất biến không cần nhà nước.

Tôi nghĩ rằng hầu hết các vấn đề phát sinh với dữ liệu lớn - các đối tượng có thể lưu, biến đổi và tự vẽ và sau đó bạn muốn chuyển nó sang trình kết xuất chẳng hạn. Vì vậy, bạn không thể sao chép nội dung của dữ liệu, vì nó quá đắt đối với một đối tượng dữ liệu lớn. Nhưng bạn cũng không muốn nó thay đổi, vì vậy bạn cần một cái gì đó như constbạn nghĩ. Hãy để trình biên dịch tìm ra lỗi thiết kế của bạn. Tuy nhiên, nếu bạn phân tách các đối tượng đó chỉ trong một đối tượng dữ liệu bất biến và triển khai các phương thức trong mô đun chịu trách nhiệm, bạn có thể chuyển xung quanh (chỉ) con trỏ và thực hiện những điều bạn muốn làm với dữ liệu trong khi vẫn đảm bảo tính bất biến.

Tôi biết rằng trong C ++ có thể khai báo một số phương thức constvà không khai báo trên các phương thức khác, vì chúng là các trình biến đổi. Và sau đó một thời gian sau bạn cần một bộ đệm và sau đó một getter sẽ biến đổi một đối tượng (vì nó lười tải) và nó không constcòn nữa. Nhưng đó là toàn bộ quan điểm của sự trừu tượng này phải không? Bạn không biết trạng thái nào bị đột biến với mỗi cuộc gọi.


1
"Vì vậy, const dường như chỉ có thể có trong các tình huống trong đó các lớp (hoặc các loại bạn muốn khai báo const) là tầm thường ..." Các cấu trúc dữ liệu liên tục là bất biến và không tầm thường. Bạn sẽ không thấy chúng trong C ++ vì hầu hết tất cả chúng đều chia sẻ dữ liệu nội bộ của chúng giữa nhiều trường hợp và C ++ thiếu bộ sưu tập rác để làm cho nó hoạt động. "Tôi nghĩ rằng bạn có thể dễ dàng làm mà không cần const nếu bạn có một thiết kế tốt." Các giá trị bất biến làm cho mã đơn giản hơn rất nhiều để lý do.
Doval

+1 cảm ơn bạn. Bạn có thể vui lòng làm rõ về "khai báo const cho một lớp"? Ý của bạn là thiết lập lớp như một const? Nếu có, tôi không hỏi về điều này - C ++ const hoạt động giống như một khung nhìn, bạn có thể khai báo một const bên cạnh đối tượng, không phải là một lớp (hoặc một cái gì đó đã thay đổi trong C ++ 11). Ngoài ra có một vấn đề với đoạn tiếp theo và từ "dễ dàng" - có một câu hỏi hay về C ++ - const trong C # và khối lượng công việc mà nó liên quan là tối quan trọng - đối với mọi loại bạn định đặt const(theo nghĩa C ++ ) bạn phải xác định giao diện, cũng cho tất cả các thuộc tính.
greenoldman

1
Khi bạn đang nói về một "lớp", bạn thực sự có nghĩa là "thể hiện của một lớp", phải không? Tôi nghĩ đó là một sự khác biệt quan trọng.
Svick
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.