Có một nhược điểm nào khi khai báo các biến với auto trong C ++ không?


143

Có vẻ như đó autolà một tính năng khá quan trọng được thêm vào trong C ++ 11 dường như đi theo rất nhiều ngôn ngữ mới hơn. Như với một ngôn ngữ như Python, tôi chưa thấy bất kỳ khai báo biến rõ ràng nào (tôi không chắc có thể sử dụng các tiêu chuẩn Python hay không).

Có một hạn chế nào trong việc sử dụng autođể khai báo các biến thay vì khai báo rõ ràng không?


1
Xem thêm: stackoverflow.com/questions/6434971/... , stackoverflow.com/questions/15254461/... , stackoverflow.com/questions/6900459/... , stackoverflow.com/questions/8430053/is-c11-auto-type-dangerous và có lẽ những người khác Không chắc đây có phải là bản sao chính xác không, nhưng chắc chắn chúng là ứng cử viên.
Cody Grey

3
Nhược điểm duy nhất tôi tìm thấy là khi tôi phải chuyển một cơ sở mã sang nền tảng (bàn điều khiển) mà trình biên dịch không hỗ trợ (và không có ý định hỗ trợ) các tính năng của C ++ 11!
Sam

7
Chỉ để hoàn thiện GotW # 94 "Hầu như luôn luôn tự động": Herbutter.com/2013/08/12/ mẹo
Richard Critten

1
Tôi đã nghe cppcast và có đề cập đến clash b / w auto và liệt kê các trình khởi tạo. Tôi sẽ cố gắng tìm podcast đó.
Abhinav Gauniyal

2
nhược điểm đầu tiên tôi nghĩ là ảnh hưởng đến tính dễ đọc của mã
ggrr

Câu trả lời:


111

Bạn chỉ hỏi về những nhược điểm, vì vậy tôi nhấn mạnh một vài trong số đó. Khi sử dụng tốt, autocó một số lợi thế là tốt. Những hạn chế là do dễ lạm dụng và tăng tiềm năng cho mã để hành xử theo những cách không lường trước được.

Hạn chế chính là, bằng cách sử dụng auto, bạn không nhất thiết phải biết loại đối tượng được tạo. Cũng có những lúc lập trình viên có thể mong đợi trình biên dịch suy ra một loại, nhưng trình biên dịch lại suy diễn một cách khéo léo một loại khác.

Đưa ra một tuyên bố như

auto result = CallSomeFunction(x,y,z);

bạn không nhất thiết phải có kiến ​​thức về loại hình result. Nó có thể là một int. Nó có thể là một con trỏ. Nó có thể là một cái gì đó khác. Tất cả những hỗ trợ hoạt động khác nhau. Bạn cũng có thể thay đổi đáng kể mã bằng một thay đổi nhỏ như

auto result = CallSomeFunction(a,y,z);

bởi vì, tùy thuộc vào những gì quá tải tồn tại cho CallSomeFunction()loại kết quả có thể hoàn toàn khác nhau - và do đó mã tiếp theo có thể hành xử hoàn toàn khác với dự định. Bạn có thể đột nhiên kích hoạt các thông báo lỗi trong mã sau này (ví dụ sau đó đang cố gắng hủy đăng ký int, cố gắng thay đổi một cái gì đó hiện tại const). Sự thay đổi độc ác hơn là nơi thay đổi của bạn lướt qua trình biên dịch, nhưng mã tiếp theo hành xử theo những cách khác nhau và không xác định - có thể là lỗi -.

Không có kiến ​​thức rõ ràng về loại của một số biến do đó làm cho khó khăn hơn để biện minh một cách nghiêm ngặt cho một tuyên bố rằng mã hoạt động như dự định. Điều này có nghĩa là có nhiều nỗ lực hơn để biện minh cho các tuyên bố "phù hợp với mục đích" trong các lĩnh vực có mức độ quan trọng cao (ví dụ như an toàn hoặc quan trọng cho nhiệm vụ).

Một nhược điểm khác, phổ biến hơn, là sự cám dỗ cho một lập trình viên sử dụng autonhư một công cụ cùn để buộc mã để biên dịch, thay vì suy nghĩ về những gì mã đang làm và làm việc để làm cho đúng.


58
Thật thú vị khi lưu ý rằng nếu các ví dụ như vậy là nhược điểm của việc sử dụng auto, thì hầu hết các ngôn ngữ gõ vịt phải chịu nhược điểm như vậy bởi thiết kế!
Leben Asa

11
Nếu CallSomeFunction()trả về một loại khác tùy thuộc vào chuỗi các đối số của nó, đó là lỗi thiết kế CallSomeFunction(), không phải là vấn đề auto. Nếu bạn không đọc tài liệu về một chức năng bạn đang sử dụng trước khi sử dụng nó, đó là một khiếm khuyết của lập trình viên, không phải là vấn đề auto. - Nhưng tôi hiểu rằng bạn đang chơi trò bênh vực của quỷ ở đây, chỉ là Nir Friedman có trường hợp tốt hơn nhiều.
DevSolar

16
@DevSolar: Tại sao sẽ T CallSomeFunction(T, int, int)là một khiếm khuyết thiết kế? Rõ ràng là nó "trả về một loại khác tùy thuộc vào chuỗi các đối số của nó."
MSalters

9
"Hạn chế chính là, bằng cách sử dụng auto, bạn không nhất thiết phải biết loại đối tượng được tạo." Bạn có thể giải thích lý do tại sao điều này là một vấn đề với auto, và không phải là một vấn đề với tạm thời subexpression? Tại sao là auto result = foo();xấu, nhưng foo().bar()không?
Angew không còn tự hào về SO

24
Có vẻ như từ các ý kiến ​​cho rằng "nhược điểm" đang được hiểu là một lý do mà một cái gì đó không thể chấp nhận được. Một nhược điểm của tính năng ngôn ngữ là một bất lợi mà nhà phát triển cần xem xét và biện minh cho việc chấp nhận hay không - tức là thực hiện đánh đổi kỹ thuật. Tôi không đưa ra tuyên bố về lý do tại sao tính năng này nên hoặc không nên được sử dụng.
Peter

76

Đây không phải là một nhược điểm của automột cách chính xác, nhưng về mặt thực tế, nó dường như là một vấn đề đối với một số người. Về cơ bản, một số người: a) coi autonhư một vị cứu tinh cho các loại và tắt não khi sử dụng nó, hoặc b) quên rằng autoluôn luôn suy luận về các loại giá trị. Điều này khiến mọi người làm những việc như thế này:

auto x = my_obj.method_that_returns_reference();

Rất tiếc, chúng tôi chỉ sao chép sâu một số đối tượng. Đó thường là một lỗi hoặc một hiệu suất thất bại. Sau đó, bạn cũng có thể xoay theo cách khác:

const auto& stuff = *func_that_returns_unique_ptr();

Bây giờ bạn có được một tài liệu tham khảo lơ lửng. Những vấn đề này hoàn toàn không gây ra auto, vì vậy tôi không coi chúng là những lý lẽ hợp pháp chống lại nó. Nhưng có vẻ như autolàm cho những vấn đề này trở nên phổ biến hơn (từ kinh nghiệm cá nhân của tôi), vì những lý do tôi liệt kê lúc đầu.

Tôi nghĩ rằng thời gian nhất định mọi người sẽ điều chỉnh và hiểu sự phân công lao động: autosuy ra kiểu cơ bản, nhưng bạn vẫn muốn nghĩ về tham chiếu và không tham gia. Nhưng nó mất một chút thời gian.


Tại sao bạn có thể sao chép sâu một đối tượng đắt tiền để bắt đầu?
Laurent LA RIZZA

3
@LaurentLARIZZA: Một số lớp có các hàm tạo sao chép đơn giản vì đôi khi chúng cần thiết (ví dụ: các thể hiện của std::vector). Trở nên đắt đỏ để sao chép không phải là một tài sản của một lớp, mà là của các đối tượng riêng lẻ. Vì vậy, method_that_returns_referencecó thể đề cập đến một đối tượng của một lớp có hàm tạo sao chép, nhưng đối tượng đó xảy ra khá tốn kém để sao chép (và không thể di chuyển từ đó).
Marc van Leeuwen

@MarcvanLeeuwen: Nếu đối tượng đắt tiền để sao chép và không thể di chuyển từ đó, tại sao nó sẽ được lưu trữ trong một std::vector? (Bởi vì nó có thể, có, hoặc vì bạn không kiểm soát lớp, nhưng đó không phải là vấn đề) Nếu việc sao chép tốn kém, (và không sở hữu tài nguyên, vì nó có thể sao chép được), tại sao không sử dụng COW trên đối tượng? Địa phương dữ liệu đã bị giết bởi kích thước của đối tượng.
Laurent LA RIZZA

2
@LaurentLARIZZA Không phải là một ví dụ của một thứ được lưu trữ trong một vectơ đắt tiền, chỉ là một vectơ thông thường <double> rất tốn kém để sao chép, đó là một phân bổ heap + O (N). Di chuyển là một cá trích đỏ. Dòng đầu tiên tôi hiển thị sẽ sao chép, không di chuyển, trừ khi tham chiếu được trả về là tham chiếu giá trị. COW thực sự không ở đây và cũng không có. Thực tế là đắt tiền để sao chép các đối tượng sẽ luôn tồn tại.
Nir Friedman

4
@Yakk Nó không thể làm điều đó một cách an toàn, bởi vì nó có thể cắt. Điều an toàn duy nhất nó có thể làm là = deletequá tải. Mặc dù nói chung hơn những gì bạn nói là một giải pháp. Đây là một chủ đề tôi đã khám phá, nếu bạn quan tâm: nirfriedman.com/2016/01/18/ .
Nir Friedman

51

Các câu trả lời khác đang đề cập đến những hạn chế như "bạn không thực sự biết loại biến là gì". Tôi muốn nói rằng điều này phần lớn liên quan đến quy ước đặt tên cẩu thả trong mã. Nếu giao diện của bạn được đặt tên rõ ràng, bạn không cần quan tâm loại chính xác là gì. Chắc chắn, auto result = callSomeFunction(a, b);không nói với bạn nhiều. Nhưng auto valid = isValid(xmlFile, schema);cho bạn biết đủ để sử dụng validmà không cần phải quan tâm loại chính xác của nó là gì. Rốt cuộc, chỉ với if (callSomeFunction(a, b)), bạn cũng sẽ không biết loại này. Tương tự với bất kỳ đối tượng tạm thời phụ khác. Vì vậy, tôi không coi đây là một nhược điểm thực sự auto.

Tôi muốn nói rằng nhược điểm chính của nó là đôi khi, kiểu trả về chính xác không phải là thứ bạn muốn làm việc. Trong thực tế, đôi khi loại trả về thực tế khác với loại trả về "logic" như một chi tiết thực hiện / tối ưu hóa. Mẫu biểu thức là một ví dụ điển hình. Hãy nói rằng chúng ta có điều này:

SomeType operator* (const Matrix &lhs, const Vector &rhs);

Logic, chúng tôi mong chờ SomeTypeđược Vector, và chúng tôi chắc chắn muốn đối xử với nó như vậy trong mã của chúng tôi. Tuy nhiên, có thể vì mục đích tối ưu hóa, thư viện đại số chúng tôi đang sử dụng các mẫu biểu thức và loại trả về thực tế là:

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

Bây giờ, vấn đề là MultExpression<Matrix, Vector>trong tất cả khả năng sẽ lưu trữ một const Matrix&const Vector&nội bộ; nó hy vọng rằng nó sẽ chuyển đổi thành Vectortrước khi kết thúc biểu thức đầy đủ của nó. Nếu chúng ta có mã này, tất cả đều tốt:

extern Matrix a, b, c;
extern Vector v;

void compute()
{
  Vector res = a * (b * (c * v));
  // do something with res
}

Tuy nhiên, nếu chúng tôi đã sử dụng autoở đây, chúng tôi có thể gặp rắc rối:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}

3
@NirFriedman Bạn nói nó rất mạnh, nhưng tôi thực sự cảm thấy autocó rất ít nhược điểm, vì vậy tôi đứng trước sức mạnh đó. Và các ví dụ khác về proxy, vv bao gồm các "trình tạo chuỗi" khác nhau và các đối tượng tương tự được tìm thấy trong DSL.
Angew không còn tự hào về SO

2
Tôi đã bị cắn bởi các mẫu biểu thức và autotrước đó, đặc biệt là với thư viện Eigen. Điều này đặc biệt khó khăn vì vấn đề thường không xuất hiện trong các bản dựng gỡ lỗi.
Dan

1
Việc sử dụng autocũng có thể cắn khi sử dụng thư viện ma trận Armadillo , sử dụng nhiều chương trình meta mẫu cho mục đích tối ưu hóa. May mắn thay, các nhà phát triển đã thêm hàm .eval () có thể được sử dụng để tránh các vấn đề vớiauto
mtall

2
"Nếu giao diện của bạn được đặt tên rõ ràng, bạn không cần quan tâm loại chính xác là gì" Trình biên dịch của bạn không thể kiểm tra tính chính xác của mã bằng cách nghiên cứu tên của các biến. Đây là toàn bộ điểm của một hệ thống loại. Mù quáng bỏ qua nó là ngớ ngẩn!
Các cuộc đua nhẹ nhàng trong quỹ đạo

1
@Angew: Đó không phải là vấn đề đối với người tạm thời bởi vì bạn thường sử dụng ngay lập tức, điều này autothường không liên quan đến một số loại kiểm tra (và văng autovào mọi nơi làm mất đi sự an toàn của loại đó giống như mọi nơi khác). Đó không phải là một so sánh tốt.
Các cuộc đua nhẹ nhàng trong quỹ đạo

13

Một trong những nhược điểm là đôi khi bạn không thể tuyên bố const_iteratorvới auto. Bạn sẽ nhận được trình lặp thông thường (không const) trong ví dụ về mã được lấy từ câu hỏi này :

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");

3
Chà, bạn nhận được một iteratortrong mọi trường hợp vì bản đồ của bạn là không const. nếu bạn muốn chuyển đổi nó thành a const_iterator, chỉ định rõ ràng loại biến như bình thường hoặc trích xuất một phương thức để bản đồ của bạn được đặt trong ngữ cảnh của bạn find. (Tôi thích cái thứ hai hơn. SRP.)
Laurent LA RIZZA

auto city_it = static_cast<const auto&>(map).find("New York")? hoặc, với C ++ 17 , auto city_if = std::as_const(map).find("New York").
Dev Null

11

Nó làm cho mã của bạn khó hơn một chút, hoặc tẻ nhạt, để đọc. Hãy tưởng tượng một cái gì đó như thế:

auto output = doSomethingWithData(variables);

Bây giờ, để tìm ra loại đầu ra, bạn phải theo dõi chữ ký của doSomethingWithDatahàm.


40
Không phải lúc nào. auto it = vec.begin();là rất dễ đọc hơn std::vector<std::wstring>::iterator it = vec.begin();ví dụ.
Jonathan Potter

4
Đã đồng ý. Nó phụ thuộc vào trường hợp sử dụng. Tôi có thể đã chính xác hơn về điều đó.
Skam

1
@SeeDart yeah, những người đang sử dụng auto như thế đang làm sai.
lciamp

6
"Theo dõi chữ ký hàm", nếu đó không phải là chuột di chuột hoặc nhấn phím ("biểu tượng theo dõi" / "đi đến khai báo" / bất cứ thứ gì nó được gọi), bạn cần phải định cấu hình trình chỉnh sửa của mình nhiều hơn hoặc chuyển sang một IDE có thể làm điều này mà không cần cấu hình ... Tuy nhiên, quan điểm của bạn vẫn hợp lệ.
hyde

6
Tôi nhận thấy rằng không phải trong một IDE mà trong các lỗ nhỏ của diffs khi xem xét các checkin! Với auto họ khó đọc hơn vì lý do đó.
JDługosz

10

Giống như nhà phát triển này , tôi ghét auto. Hay đúng hơn, tôi ghét cách mọi người sử dụng sai auto.

Tôi có ý kiến ​​(mạnh mẽ) autolà để giúp bạn viết mã chung chung, không phải để giảm việc gõ .
C ++ là ngôn ngữ có mục tiêu là cho phép bạn viết mã mạnh mẽ, không giảm thiểu thời gian phát triển.
Điều này khá rõ ràng từ nhiều tính năng của C ++, nhưng thật không may, một vài trong số những tính năng mới hơn như thế autolàm giảm việc đánh lừa mọi người nghĩ rằng họ nên bắt đầu lười biếng với việc gõ.

Trước autođây, mọi người đã sử dụng typedefs, điều này thật tuyệt vời vì typedef cho phép người thiết kế thư viện giúp bạn tìm ra loại trả về nên là gì, để thư viện của họ hoạt động như mong đợi. Khi bạn sử dụng auto, bạn lấy đi quyền điều khiển đó từ trình thiết kế của lớp và thay vào đó, hãy yêu cầu trình biên dịch tìm ra loại nên là gì, loại bỏ một trong những công cụ C ++ mạnh nhất khỏi hộp công cụ và có nguy cơ phá vỡ mã của chúng.

Nói chung, nếu bạn sử dụng auto, thì đó là do mã của bạn hoạt động với bất kỳ loại hợp lý nào , không phải vì bạn quá lười để viết ra loại mà nó nên hoạt động. Nếu bạn sử dụng autonhư một công cụ để giúp đỡ sự lười biếng, thì điều xảy ra là cuối cùng bạn bắt đầu đưa ra các lỗi tinh vi trong chương trình của mình, thường là do các chuyển đổi ngầm không xảy ra do bạn đã sử dụng auto.

Thật không may, các lỗi này rất khó để minh họa trong một ví dụ ngắn ở đây vì tính ngắn gọn của chúng khiến chúng kém thuyết phục hơn so với các ví dụ thực tế xuất hiện trong dự án người dùng - tuy nhiên, chúng xảy ra dễ dàng trong mã nặng mẫu mong đợi các chuyển đổi ngầm định sẽ thực hiện địa điểm.

Nếu bạn muốn một ví dụ, có một ở đây . Mặc dù vậy, một lưu ý nhỏ: trước khi bị cám dỗ nhảy và chỉ trích mã: hãy nhớ rằng nhiều thư viện nổi tiếng và trưởng thành đã được phát triển xung quanh các chuyển đổi ngầm định đó và chúng ở đó vì chúng giải quyết các vấn đề có thể khó khăn nếu không phải là không thể để giải quyết khác. Hãy cố gắng tìm ra một giải pháp tốt hơn trước khi chỉ trích họ.


3
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should beKhông thực sự là một lý do tốt IMO. Ví dụ, cập nhật IDE, Visual Studio 2015, cho phép bạn kiểm tra loại biến bằng cách di chuột qua auto. Đây là * chính xác * giống như typedefmột.
Gà Sombrero

@JameyD: Bạn đang thiếu một số điểm quan trọng ở đó: (1) Đối số IDE của bạn chỉ hoạt động nếu loại cụ thể, không được tạo khuôn mẫu. IDE có thể không thể cho bạn biết loại chính xác trong trường hợp loại phụ thuộc, ví dụ typename std::iterator_traits<It>::value_type. (2) Toàn bộ vấn đề là loại suy ra không cần phải "hoàn toàn giống" như loại chính xác mà nhà thiết kế mã trước đó dự định; bằng cách sử dụng auto, bạn sẽ lấy đi khả năng của nhà thiết kế để chỉ định loại chính xác.
dùng541686

Về cơ bản, bạn đang nói về proxy, một trong những câu trả lời đã đề cập. Các mẫu biểu thức và vectơ <bool> vô nghĩa không phải là mã hàng ngày đối với hầu hết mọi người. Trong hầu hết các tình huống, bạn không muốn chuyển đổi ngầm định và tự động giúp với điều đó. Herb Sutter nói về những lợi thế của tự động rộng rãi trong một trong những bài đăng trên blog của anh ấy, và nó không chủ yếu là về tổ hợp phím, và nó cũng không chỉ dành cho mã chung. Ngoài ra, liên kết đầu tiên bạn cung cấp, bài đăng trên blog chỉ đơn giản là lời khuyên khủng khiếp (đó là lý do tại sao anh ấy chỉ trích mạnh mẽ trong phần bình luận của mình).
Nir Friedman

@NirFriedman: "... vector<bool>vô nghĩa" ... xin lỗi? Bạn nghĩ như thế nào bitsetđược thực hiện? Hay bạn coi các container bit là hoàn toàn vô nghĩa?!
dùng541686

1
@NirFriedman: Không có gì về vector <bool> là tin tức với tôi. Điều tôi đang cố nói với bạn và rằng bạn đang từ chối hiểu một cách trắng trợn là vì mục đích của câu hỏi này không khác gì vectơ <bool> - cả hai đều sử dụng proxy, vì các proxy được coi là hữu ích và thực tế là proxy rất hữu ích là một thực tế bạn cần chấp nhận thay vì sống trong sự phủ nhận. Bạn có thể vui lòng ngừng biến điều này thành một cuộc tranh luận về việc bạn có nghĩ rằng proxy là hữu ích không? Đó không phải là chủ đề tranh luận, và cũng vậy, ý kiến ​​của bạn về họ chỉ là ý kiến ​​của bạn, không phải là một loại thực tế.
dùng541686

6

autokhông có nhược điểm cho mỗi gia nhập , và tôi ủng hộ để (tay wavily) sử dụng nó ở khắp mọi nơi trong mã mới. Nó cho phép mã của bạn kiểm tra kiểu một cách nhất quán và nhất quán tránh bị cắt im lặng. (Nếu Bxuất phát từ Avà một hàm trả về Ađột ngột trả về B, thì autohoạt động như mong đợi để lưu trữ giá trị trả về của nó)

Mặc dù, mã kế thừa tiền C ++ 11 có thể dựa vào các chuyển đổi ngầm định gây ra bởi việc sử dụng các biến được gõ rõ ràng. Thay đổi một biến được gõ rõ ràng để autocó thể thay đổi hành vi mã , vì vậy tốt hơn hết bạn nên thận trọng.


Downvote là công bằng, nhưng bạn có thể vui lòng bình luận tại sao?
Laurent LA RIZZA

Tôi đã không đánh giá thấp bạn, nhưng autonó có nhược điểm (hoặc ít nhất - nhiều người nghĩ vậy). Xem xét ví dụ được đưa ra trong câu hỏi thứ hai trong cuộc thảo luận nhóm này với Sutter, Alexandrescu và Meyers: Nếu bạn có auto x = foo(); if (x) { bar(); } else { baz(); }foo()trả lại bool- điều gì xảy ra nếu foo()thay đổi để trả lại một enum (ba tùy chọn thay vì hai)? Các automã sẽ tiếp tục làm việc, nhưng tạo ra kết quả bất ngờ.
einpoklum

@einpoklum: Và có sử dụng boolthay vì thay autođổi bất cứ điều gì trong trường hợp enum không được kiểm soát? Tôi có thể sai (không thể kiểm tra ở đây), nhưng tôi nghĩ rằng sự khác biệt duy nhất là việc chuyển đổi boolsẽ xảy ra khi khai báo thay vì đánh giá điều kiện trong if. Nếu enumphạm vi là phạm vi, thì chuyển đổi thành boolsẽ không xảy ra mà không có thông báo rõ ràng.
Laurent LA RIZZA

4

Từ khóa autochỉ cần suy ra loại từ giá trị trả về. Do đó, nó không tương đương với một đối tượng Python, vd

# Python
a
a = 10       # OK
a = "10"     # OK
a = ClassA() # OK

// C++
auto a;      // Unable to deduce variable a
auto a = 10; // OK
a = "10";    // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0;    // OK, implicit casting warning

autođược suy luận trong quá trình biên dịch, nó sẽ không có bất kỳ nhược điểm nào trong thời gian chạy.


1
vâng, về cơ bản nó làm những gì type()trong python. Nó suy ra kiểu, nó không tạo ra một biến mới của kiểu đó.
lciamp

2
@lciamp Thật ra, đó sẽ là decltype. autolà để gán biến cụ thể.
Khối

4

Điều mà không ai đề cập ở đây cho đến nay, nhưng đối với bản thân nó là giá trị câu trả lời nếu bạn hỏi tôi.

Vì (ngay cả khi mọi người nên biết rằng C != C++) mã được viết bằng C có thể dễ dàng được thiết kế để cung cấp cơ sở cho mã C ++ và do đó được thiết kế mà không cần quá nhiều nỗ lực để tương thích với C ++, đây có thể là một yêu cầu cho thiết kế.

Tôi biết về một số quy tắc trong đó một số cấu trúc được xác định rõ Clà không hợp lệ C++và ngược lại. Nhưng điều này chỉ đơn giản là dẫn đến các thực thi bị hỏng và mệnh đề UB đã biết áp dụng mà hầu hết các lần được chú ý bởi các vòng lặp lạ dẫn đến sự cố hoặc bất cứ điều gì (hoặc thậm chí có thể không bị phát hiện, nhưng điều đó không quan trọng ở đây).

Nhưng đây autolà lần đầu tiên 1 thay đổi này!

Hãy tưởng tượng bạn đã sử dụng autolàm công cụ xác định lớp lưu trữ trước đó và chuyển mã. Nó thậm chí sẽ không nhất thiết (tùy thuộc vào cách nó được sử dụng) "phá vỡ"; nó thực sự có thể âm thầm thay đổi hành vi của chương trình.

Đó là điều người ta nên ghi nhớ.


1 Ít nhất là lần đầu tiên tôi biết.


1
Dù sao bạn cũng sẽ gặp lỗi biên dịch khi cố gắng biên dịch.
Gà Sombrero

@JameyD: Điều gì sẽ làm như vậy? Tại sao 2 tình huống mã hợp lệ với ý nghĩa khác nhau lại bị lỗi?
dhein

8
Nếu bạn đang dựa vào "không có loại ngụ ý int" trong C, bạn xứng đáng với tất cả những thứ tồi tệ mà bạn sẽ nhận được từ điều này. Và nếu bạn không dựa vào nó, sử dụng autonhư một công cụ xác định lớp lưu trữ cùng với một loại sẽ cung cấp cho bạn một lỗi biên dịch đẹp trong C ++ (đây là một điều tốt trong trường hợp này).
Angew không còn tự hào về SO

1
@Angew đó là trường hợp tôi đang đề cập đến, yeah. Tôi không làm điều này. Nhưng đó là điều ít nhất nên được ghi nhớ.
dhein

3

Một lý do mà tôi có thể nghĩ đến là bạn mất cơ hội cưỡng chế lớp học được trả lại. Nếu chức năng hoặc phương thức của bạn trả về 64 bit dài và bạn chỉ muốn một số nguyên không dấu 32, thì bạn sẽ mất cơ hội kiểm soát điều đó.


1
Có static_cast và IIRC ví dụ C ++ hiện đại hiệu quả của Meyers thậm chí còn khuyên bạn nên sử dụng nó để chỉ định loại cho biến tự động gõ.
hyde

2

Như tôi đã mô tả trong câu trả lời này auto đôi khi có thể dẫn đến những tình huống thú vị mà bạn không có ý định. Bạn phải nói auto&một cách rõ ràng là có một kiểu tham chiếu trong khi thực hiện chỉ autocó thể tạo một loại con trỏ. Điều này có thể dẫn đến sự nhầm lẫn bằng cách bỏ qua tất cả các công cụ xác định cùng nhau, dẫn đến một bản sao của tài liệu tham khảo thay vì một tài liệu tham khảo thực tế.


2
Đó không phải là sôi nổi. Đó là những gì autokhông, không bao giờ suy ra một tài liệu tham khảo cũng như constloại. Để autotham khảo, bạn nên sử dụng tốt hơn auto&&. (một tài liệu tham khảo phổ quát) Nếu loại không rẻ để sao chép hoặc sở hữu tài nguyên, thì loại này không thể được sao chép để bắt đầu.
Laurent LA RIZZA

1

Một ví dụ khó chịu khác:

for (auto i = 0; i < s.size(); ++i)

tạo một cảnh báo ( comparison between signed and unsigned integer expressions [-Wsign-compare]), vì ilà một int đã ký. Để tránh điều này, bạn cần phải viết ví dụ

for (auto i = 0U; i < s.size(); ++i)

hoặc có lẽ tốt hơn:

for (auto i = 0ULL; i < s.size(); ++i)

1
Có tôi thấy điều này gây khó chịu quá. Nhưng lỗ hổng trong ngôn ngữ là một nơi khác. Để mã này thực sự di động, giả sử sizetrả về size_t, bạn phải có khả năng có size_tnghĩa đen 0z. Nhưng bạn có thể khai báo một UDL để làm điều này. ( size_t operator""_z(...))
Laurent LA RIZZA

1
Sự phản đối hoàn toàn về mặt lý thuyết: unsigneddường như không đủ lớn để chứa tất cả các giá trị của std::size_tkiến trúc chính thống, do đó, trong trường hợp không ai có một thùng chứa với số lượng phần tử khổng lồ, sử dụng unsignedcó thể gây ra một vòng lặp vô hạn trên phạm vi thấp hơn của các chỉ số. Mặc dù điều này không chắc là một vấn đề, std::size_tnên được sử dụng để có được mã sạch báo hiệu đúng ý định. Tôi không chắc chắn thậm chí còn unsigned long longđược đảm bảo nghiêm ngặt, mặc dù trong thực tế, nó có lẽ phải giống nhau.
gạch dưới

@underscore_d: có, điểm công bằng - unsigned long longđược đảm bảo ít nhất là 64 bit, nhưng theo lý thuyết size_tcó thể lớn hơn thế này, tôi cho rằng. Tất nhiên, nếu bạn có> 2 ^ 64 yếu tố trong thùng chứa thì bạn có thể gặp vấn đề lớn hơn để lo lắng về ... ;-)
Paul R

1

Tôi nghĩ autolà tốt khi được sử dụng trong một bối cảnh cục bộ, nơi người đọc dễ dàng & rõ ràng có thể khấu trừ loại của nó, hoặc tài liệu tốt với một nhận xét về loại của nó hoặc một tên suy ra loại thực tế. Những người không hiểu làm thế nào nó hoạt động có thể đưa nó theo những cách sai, như sử dụng nó thay vì templatehoặc tương tự. Dưới đây là một số trường hợp sử dụng tốt và xấu theo ý kiến ​​của tôi.

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

Sử dụng tốt

Lặp đi lặp lại

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

Chức năng con trỏ

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

Sử dụng xấu

Dòng dữ liệu

auto input = "";

..

auto output = test(input);

Chữ ký chức năng

auto test (auto a, auto b, auto c)
{
    ..
}

Trường hợp tầm thường

for(auto i = 0; i < 100; i++)
{
    ..
}

Khi bạn muốn một int, bạn phải gõ thêm một char nếu bạn đi auto. Điều đó không thể chấp nhận được
Rerito

@Rerito vâng, nó intdễ thấy như ở đây & gõ intthì ngắn hơn. Đó là lý do tại sao nó là một trường hợp tầm thường.
Khaled.K

0

Tôi ngạc nhiên không ai đề cập đến điều này, nhưng giả sử bạn đang tính giai thừa của một cái gì đó:

#include <iostream>
using namespace std;

int main() {
    auto n = 40;
    auto factorial = 1;

    for(int i = 1; i <=n; ++i)
    {
        factorial *= i;
    }

    cout << "Factorial of " << n << " = " << factorial <<endl;   
    cout << "Size of factorial: " << sizeof(factorial) << endl; 
    return 0;
}

Mã này sẽ xuất ra điều này:

Factorial of 40 = 0
Size of factorial: 4

Đó chắc chắn không phải là kết quả mong đợi. Điều đó xảy ra bởi vì autosuy ra loại giai thừa biến intvì nó được gán cho 1.

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.