Tại sao việc sử dụng bộ dữ liệu trong C ++ không phổ biến hơn?


124

Tại sao dường như không ai sử dụng bộ dữ liệu trong C ++, Thư viện Boost Tuple hoặc thư viện chuẩn cho TR1? Tôi đã đọc rất nhiều mã C ++ và rất hiếm khi tôi thấy việc sử dụng các bộ dữ liệu, nhưng tôi thường thấy rất nhiều nơi mà các bộ dữ liệu sẽ giải quyết nhiều vấn đề (thường trả về nhiều giá trị từ các hàm).

Tuples cho phép bạn làm tất cả những thứ hay ho như thế này:

tie(a,b) = make_tuple(b,a); //swap a and b

Điều đó chắc chắn tốt hơn thế này:

temp=a;
a=b;
b=temp;

Tất nhiên bạn luôn có thể làm điều này:

swap(a,b);

Nhưng nếu bạn muốn xoay ba giá trị thì sao? Bạn có thể làm điều này với bộ dữ liệu:

tie(a,b,c) = make_tuple(b,c,a);

Tuples cũng làm cho việc trả lại nhiều biến từ một hàm dễ dàng hơn nhiều, đây có thể là trường hợp phổ biến hơn nhiều so với các giá trị tráo đổi. Sử dụng tài liệu tham khảo để trả về giá trị chắc chắn không phải là rất thanh lịch.

Có bất kỳ nhược điểm lớn nào đối với các bộ dữ liệu mà tôi không nghĩ đến không? Nếu không, tại sao chúng hiếm khi được sử dụng? Họ có chậm hơn không? Hay chỉ là mọi người không quen với họ? Nó có phải là một ý tưởng tốt để sử dụng bộ dữ liệu?


17
+1 cho thủ thuật hoán đổi tuple thông minh :)
kizzx2

10
a = a ^ b; b = a ^ b; a = a ^ b;
Gerardo Marset

3
Bộ dữ liệu IMO thuận tiện trong các ngôn ngữ gõ yếu hoặc ngôn ngữ mà chúng là cấu trúc gốc. Ví dụ, trong Python hoặc PHP, chúng chỉ làm cho cuộc sống dễ dàng hơn, trong khi trong C ++ có quá nhiều cách gõ (để xây dựng nó từ khuôn mẫu) và quá ít lợi ích.
tài liệu

4
Một nhận xét cho OP: Tôi nghĩ rằng câu trả lời được chấp nhận hiện tại đã bị lỗi thời đến mức thực sự sai. Bạn có thể muốn xem xét lại sự lựa chọn của câu trả lời được chấp nhận.
ulidtko

8
@GerardoMarset Bạn có nghiêm túc không?
thesaint

Câu trả lời:


43

Bởi vì nó chưa chuẩn. Bất cứ điều gì không chuẩn có một trở ngại cao hơn nhiều. Pieces of Boost đã trở nên phổ biến vì các lập trình viên đang kêu gọi họ. (hash_map nhảy vào tâm trí). Nhưng trong khi tuple là tiện dụng, nó không phải là một chiến thắng quá lớn và rõ ràng mà mọi người bận tâm với nó.


1
Mọi người dường như sử dụng các phần khác của Boost như điên. Mặc dù các bản đồ băm nói chung hữu ích hơn nhiều so với các bộ dữ liệu.
Zifre

Tôi không biết chi tiết cụ thể về những gì bạn đang thấy, nhưng tôi đoán rằng những phần mọi người đang sử dụng như điên là những tính năng họ thực sự, thực sự muốn. Do đó (một lần nữa, đoán) mức độ phổ biến của bản đồ băm, con trỏ đếm và tương tự. Bộ tuple rất tiện dụng, nhưng nó không phải là thứ gì đó nhảy ra để lấp lỗ hổng. Tiền chi trả không rõ ràng. Bao lâu thì bạn cần xoay chính xác N đối tượng? (Trái ngược với việc cần phải xoay các vectơ dài tùy ý). Và mọi người đã quen với việc chuyển các giá trị trả về bằng tham chiếu hoặc trả về các lớp hoặc cấu trúc nhỏ.
Alan De Smet

20
Hiện tại nó là một phần của tiêu chuẩn C ++ 11: en.cppreference.com/w/cpp/utility/tuple
Roman Susi

124

Một câu trả lời cay độc là nhiều người lập trình trong C ++, nhưng không hiểu và / hoặc sử dụng chức năng cấp cao hơn. Đôi khi, vì họ không được phép, nhưng nhiều người chỉ đơn giản là không thử (hoặc thậm chí không hiểu).

Như một ví dụ không tăng: có bao nhiêu người sử dụng chức năng được tìm thấy trong <algorithm>?

Nói cách khác, nhiều lập trình viên C ++ chỉ đơn giản là lập trình viên C sử dụng trình biên dịch C ++, và có lẽ std::vectorstd::list. Đó là một lý do tại sao việc sử dụng boost::tuplekhông phổ biến hơn.


18
-1 từ tôi bởi vì các lập trình viên C ++ không ngu ngốc như câu trả lời này làm cho họ nghe được.
dùng541686

5
@Mehrdad Đã xem qua rất nhiều mã C ++, cả thương mại và không, đọc hàng tấn tài liệu C ++, tôi nghĩ khá an toàn khi nói rằng một phần rất lớn các nhà phát triển "C ++" chỉ là các nhà phát triển C không thể hiểu được Trình biên dịch C. Ví dụ, các mẫu gần như hoàn toàn bị thiếu trong hầu hết các tài liệu (điều mà tôi đã học được để yêu rất nhiều). Hacks macro lạ là phổ biến và không gian tên được sử dụng nghiêm trọng.
Rõ ràng hơn

5
Câu trả lời vô nghĩa. Nếu ai đó cần chúng, họ sẽ bắt kịp chúng. Chúng không cần thiết; vì vậy chúng không được sử dụng Nói rằng chúng không được sử dụng bởi vì chúng không dễ hiểu là xấu.
Michael Chourdakis

9
@Michael Bình luận vô nghĩa. Không có gì là cần thiết trong lập trình khi bạn đã có một tập hợp con hoàn chỉnh của ngôn ngữ. Thiếu sử dụng chắc chắn không ngụ ý rằng mọi người đều hiểu các cấu trúc C ++ cấp cao hơn và chọn không sử dụng chúng.
Trey Jackson

4
Tbh tôi KHÔNG BAO GIỜ có nhu cầu về std :: tuple bên ngoài siêu mẫu khuôn mẫu. Không có lý do nào trong cuộc sống khi tôi ngồi xuống với khuôn mặt buồn bã khi nghĩ rằng "Giá như tôi có những bộ dữ liệu đó". Trong thực tế khi tôi nhìn vào các bộ dữ liệu, tôi nghĩ rằng "cái quái gì mà một người lành mạnh sẽ cần chúng để làm gì (tôi sẽ không coi mình là lành mạnh)". Những người bên ngoài lập trình meta dường như sử dụng chúng như là "Cấu trúc ẩn danh" rất xấu và cho thấy sự thiếu vắng mạnh mẽ về chất lượng mã và khả năng bảo trì trong đầu của họ.
thesaint

23

Cú pháp tuple C ++ có thể dài hơn một chút so với hầu hết mọi người mong muốn.

Xem xét:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

Vì vậy, nếu bạn muốn sử dụng rộng rãi các bộ dữ liệu, bạn có thể nhận được các bộ dữ liệu ở khắp mọi nơi hoặc bạn có được các tên loại dài khó chịu ở mọi nơi. Tôi thích tuples. Tôi sử dụng chúng khi cần thiết. Nhưng nó thường bị giới hạn trong một vài tình huống, như chỉ số phần tử N hoặc khi sử dụng đa khung để buộc các cặp vòng lặp phạm vi. Và nó thường trong một phạm vi rất hạn chế.

Tất cả đều trông rất xấu xí và đáng sợ khi được so sánh với một thứ như Haskell hoặc Python. Khi C ++ 0x đến đây và chúng tôi nhận được các bộ từ khóa 'tự động' sẽ bắt đầu trông hấp dẫn hơn rất nhiều.

Tính hữu dụng của bộ dữ liệu tỷ lệ nghịch với số lượng tổ hợp phím cần thiết để khai báo, đóng gói và giải nén chúng.


Hầu hết mọi người sẽ làm "sử dụng tăng cường không gian tên;" và không phải gõ boost ::. Tôi không nghĩ gõ tuple là vấn đề lớn. Điều đó nói rằng, tôi nghĩ rằng bạn có một điểm. tự động có thể làm cho nhiều người hơn bắt đầu sử dụng bộ dữ liệu.
Zifre

2
@Zifre: vấn đề là bạn không nên "sử dụng không gian tên X" trong tệp tiêu đề vì nó buộc ô nhiễm không gian tên và thay đổi không gian tên.
Ông Fooz

1
À, vâng, tôi quên mất tiêu đề. Nhưng bên trong mã chương trình, bạn không cần phải lo lắng về nó. Và một khi chúng ta có C ++ 0x, chúng ta có thể sử dụng auto sẽ loại bỏ rất nhiều thao tác gõ.
Zifre

19
Chỉ tôi thôi à? Tôi không nghĩ việc lưu 7 ký tự của "boost ::" là những gì anh ấy đang đề cập, mà là 33 ký tự khác . Đó là một cái quái của rất nhiều kiểu gõ tên, đặc biệt là nếu những cái đó cũng nằm trong phạm vi không gian tên. Lấy boost :: tuple <std :: string, std :: set <std :: string>, std :: vector <My :: Scoped :: LongishTypeName >> làm ví dụ vô lý.
Ogre Psalm33

10

Đối với tôi, đó là thói quen, nương tay: Tuples không giải quyết bất kỳ vấn đề mới nào cho tôi, chỉ một vài điều tôi đã có thể xử lý tốt. Trao đổi giá trị vẫn cảm thấy dễ dàng hơn theo cách lỗi thời - và quan trọng hơn, tôi không thực sự nghĩ về cách trao đổi "tốt hơn". Nó đủ tốt như vốn có.

Cá nhân, tôi không nghĩ rằng bộ dữ liệu là một giải pháp tuyệt vời để trả về nhiều giá trị - nghe có vẻ như là một công việc cho structs.


4
'Tôi không thực sự nghĩ về cách trao đổi "tốt hơn."' - Khi tôi viết mã, tôi viết lỗi. Giảm độ phức tạp của mã làm giảm số lượng lỗi tôi viết. Tôi ghét tạo ra các lỗi tương tự lặp đi lặp lại. Có, tôi nghĩ về cách mã hóa <strike> hoán đổi </> tốt hơn . Ít bộ phận chuyển động hơn (LỘC, biến tạm thời, định danh để nhập nhầm), mã dễ đọc hơn; Mã tốt.
sehe

Đồng ý. Một lớp được bọc trong con trỏ tự động hoặc con trỏ thông minh là kiểu lưu. Tôi đã sử dụng bộ dữ liệu một lần, nhưng sau đó viết lại mã bằng các lớp. retValue.state rõ ràng hơn retValue.get <0> ().
Valentin Heinitz

1
@sehe: Viết mã tốt hơn, dễ đọc hơn cũng là mục tiêu của tôi. Thêm nhiều loại cú pháp có chi phí và tôi không tin "hoán đổi tốt hơn" biện minh cho chi phí tinh thần khi nghĩ về nhiều loại cú pháp hơn cho mỗi dòng mã bạn đọc.
ojrac

8

Nhưng nếu bạn muốn xoay ba giá trị thì sao?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

OK, vì vậy với 4 giá trị vv, cuối cùng n-tuple trở thành ít mã hơn so với hoán đổi n-1. Và với hoán đổi mặc định, nó thực hiện 6 bài tập thay vì 4 bài bạn có nếu bạn tự thực hiện một mẫu ba chu kỳ, mặc dù tôi hy vọng trình biên dịch sẽ giải quyết điều đó cho các kiểu đơn giản.

Bạn có thể đưa ra các tình huống trong đó các giao dịch hoán đổi khó sử dụng hoặc không phù hợp, ví dụ:

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

là một chút vụng về để giải nén.

Mặc dù vậy, vấn đề là có nhiều cách xử lý các tình huống phổ biến nhất mà các bộ dữ liệu là tốt, và do đó không có sự khẩn cấp lớn nào để sử dụng các bộ dữ liệu. Nếu không có gì khác, tôi không tự tin rằng:

tie(a,b,c) = make_tuple(b,c,a);

không làm 6 bản sao, làm cho nó hoàn toàn không phù hợp với một số loại (bộ sưu tập là rõ ràng nhất). Hãy thuyết phục tôi rằng các bộ dữ liệu là một ý tưởng tốt cho các loại "lớn", bằng cách nói điều này không phải vậy :-)

Để trả về nhiều giá trị, bộ dữ liệu là hoàn hảo nếu các giá trị thuộc loại không tương thích, nhưng một số người không thích chúng nếu người gọi có thể đưa chúng theo thứ tự sai. Một số người không thích nhiều giá trị trả về, và không muốn khuyến khích việc sử dụng chúng bằng cách làm cho chúng dễ dàng hơn. Một số người chỉ thích các cấu trúc được đặt tên cho các tham số vào và ra, và có lẽ không thể bị thuyết phục với một cây gậy bóng chày để sử dụng bộ dữ liệu. Không có kế toán cho hương vị.


1
Bạn chắc chắn sẽ không muốn trao đổi vectơ với bộ dữ liệu. Tôi nghĩ việc hoán đổi ba yếu tố chắc chắn rõ ràng hơn với các bộ dữ liệu so với hai lần hoán đổi. Đối với nhiều giá trị trả về, các tham số out là xấu, các cấu trúc được gõ thêm và chắc chắn có trường hợp cần nhiều giá trị trả về.
Zifre

biết lý do tại sao bạn sử dụng bộ dữ liệu (và tôi biết lý do tại sao tôi sử dụng chúng nếu có dịp, mặc dù tôi không nghĩ nó đã từng có). Tôi đoán tại sao người khác không sử dụng chúng ngay cả khi họ biết về chúng. ví dụ: vì họ không đồng ý với "các thông số xấu xa" ...
Steve Jessop

Bạn có biết nếu chúng ta có thể thay thế "tie (a, b, c) = make_tuple (b, c, a);" bởi "tie (a, b, c) = tie (b, c, a);" ?
Rexxar

2
Cà vạt (tốt, một bậc kỹ thuật) là một tuple được thực hiện với các tham chiếu không phải là const. Tôi không thể tìm thấy tài liệu tăng cường cho biết điều gì đảm bảo toán tử = và hàm tạo sao chép cho tie / tuple tạo ra khi một số tài liệu tham khảo có cùng tham chiếu. Nhưng đó là những gì bạn cần biết. Việc triển khai toán tử ngây thơ = rõ ràng có thể rất sai ...
Steve Jessop

1
@Steve: Và kể từ khi quan hệ là tất cả về việc ngăn chặn bản sao (họ nên làm việc với nhiều loại phi copyable; lưu ý rằng LHS có thể là một cái gì đó khác hoàn toàn) thực sự tất cả nên đi rất sai (suy nghĩ của các đối tượng lớp không POD). Chỉ cần tưởng tượng cách bạn viết cùng một logic mà không cần sử dụng temps.
sehe

7

Như nhiều người đã chỉ ra, bộ dữ liệu không hữu dụng như các tính năng khác.

  1. Các mánh lới hoán đổi và xoay chỉ là mánh lới quảng cáo. Chúng hoàn toàn khó hiểu với những người chưa từng nhìn thấy chúng trước đây và vì nó có khá nhiều người, những mánh lới quảng cáo này chỉ là thực hành kỹ thuật phần mềm kém.

  2. Trả về nhiều giá trị bằng cách sử dụng các bộ dữ liệu ít hơn là tự ghi lại các lựa chọn sau đó - trả về các kiểu được đặt tên hoặc sử dụng các tham chiếu được đặt tên. Nếu không có tài liệu này, rất dễ nhầm lẫn thứ tự của các giá trị được trả về, nếu chúng có thể chuyển đổi lẫn nhau và không thể khôn ngoan hơn.


6

Không phải ai cũng có thể sử dụng boost và TR1 chưa có sẵn rộng rãi.


3
Rất nhiều người sử dụng Boost. Những người đó cũng có thể sử dụng bộ dữ liệu.
Zifre

3
Bạn hỏi tại sao mọi người không sử dụng chúng, và tôi đã đưa ra một câu trả lời.
Brian Neal

2
Đối với cử tri xuống: Tôi tình cờ làm việc ở một nơi không thể sử dụng boost về mặt chính trị và ngay cả vào thời điểm này, chuỗi công cụ biên dịch chúng tôi sử dụng (đối với hệ thống nhúng) không hỗ trợ TR1 / C ++ 11.
Brian Neal

5

Khi sử dụng C ++ trên các hệ thống nhúng, việc kéo vào thư viện Boost trở nên phức tạp. Họ cặp với nhau, vì vậy kích thước thư viện phát triển. Bạn trả về cấu trúc dữ liệu hoặc sử dụng truyền tham số thay vì bộ dữ liệu. Khi trả về các bộ dữ liệu trong Python, cấu trúc dữ liệu theo thứ tự và loại của các giá trị được trả về, nó không rõ ràng.


5

Bạn hiếm khi nhìn thấy chúng bởi vì mã được thiết kế tốt thường không cần đến chúng - không có nhiều trường hợp trong tự nhiên khi sử dụng cấu trúc ẩn danh vượt trội hơn so với sử dụng mã được đặt tên. Vì tất cả các tuple thực sự đại diện là một cấu trúc ẩn danh, hầu hết các lập trình viên trong hầu hết các tình huống chỉ đi với thực tế.

Giả sử chúng ta có hàm "f" trong đó trả về tuple có thể có ý nghĩa. Theo nguyên tắc chung, các chức năng như vậy thường đủ phức tạp để chúng có thể thất bại.

Nếu "f" CÓ THỂ thất bại, bạn cần trả về trạng thái - sau tất cả, bạn không muốn người gọi phải kiểm tra mọi tham số để phát hiện lỗi. "f" có thể phù hợp với mẫu:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

Điều đó không đẹp, nhưng hãy nhìn xem sự thay thế xấu xí như thế nào. Lưu ý rằng tôi vẫn cần một giá trị trạng thái, nhưng mã không thể đọc được và không ngắn hơn. Có lẽ nó cũng chậm hơn, vì tôi phải chịu chi phí cho 1 bản sao với bộ dữ liệu.

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

Một nhược điểm đáng kể khác được ẩn giấu ở đây - với "ReturnInts" Tôi có thể thêm sự thay đổi của "f" bằng cách sửa đổi "ReturnInts" mà không thay đổi giao diện "f". Giải pháp tuple không cung cấp tính năng quan trọng đó, khiến nó trở thành câu trả lời kém hơn cho bất kỳ mã thư viện nào.


1
Ngoại lệ làm cho giao diện đó sạch hơn nhiều.
David Stone

Để công bằng (và cực kỳ muộn cho bữa tiệc), bạn có thể dễ dàng đọc bằng cách cài đặt using std::tuple;và chỉ sử dụng tupletrong mã.
Alrekr

2
Sử dụng tuplelàm cho mã ít đọc hơn , không nhiều hơn. Hầu hết các mã ngày nay có một số lượng lớn các ký hiệu trong đó - nhìn thấy std::tuplelàm cho nó rõ ràng chính xác nó là gì.
Tom Swirly

3

Chắc chắn các bộ dữ liệu có thể hữu ích, nhưng như đã đề cập, có một chút chi phí và một hoặc hai chướng ngại vật bạn phải nhảy qua trước khi bạn thực sự có thể sử dụng chúng.

Nếu chương trình của bạn luôn tìm thấy những nơi bạn cần trả về nhiều giá trị hoặc trao đổi một vài giá trị, thì có thể đáng để đi theo con đường tuple, nhưng nếu không, đôi khi việc thực hiện mọi thứ theo cách cổ điển sẽ dễ dàng hơn.

Nói chung, không phải tất cả mọi người đã cài đặt Boost và tôi chắc chắn sẽ không gặp rắc rối khi tải xuống và định cấu hình các thư mục bao gồm của tôi để làm việc với nó chỉ cho các tiện ích tuple của nó. Tôi nghĩ rằng bạn sẽ thấy rằng những người đã sử dụng Boost có nhiều khả năng tìm thấy việc sử dụng tuple trong các chương trình của họ hơn những người dùng không phải Boost và những người di cư từ các ngôn ngữ khác (Python nghĩ đến) có vẻ đơn giản là khó chịu về việc thiếu bộ dữ liệu trong C ++ hơn là khám phá các phương pháp thêm hỗ trợ tuple.


1

Là một kho lưu trữ dữ liệu std::tuplecó các đặc điểm tồi tệ nhất của cả a structvà mảng; tất cả quyền truy cập là vị trí thứ n nhưng người ta không thể lặp lại thông qua tupleviệc sử dụng forvòng lặp.

Vì vậy, nếu các phần tử trong tuplekhái niệm là một mảng, tôi sẽ sử dụng một mảng và nếu các phần tử không phải là một mảng về mặt khái niệm, một cấu trúc (có các phần tử được đặt tên) sẽ dễ duy trì hơn. ( a.lastnamelà giải thích nhiều hơn std::get<1>(a)).

Điều này để lại sự chuyển đổi được OP đề cập như là usecase khả thi duy nhất cho các bộ dữ liệu.


0

Tôi có cảm giác rằng nhiều người sử dụng Boost.Any và Boost.Variant (với một số kỹ thuật) thay vì Boost.Tuple.


Tại sao bạn giao dịch gõ tĩnh hiệu quả cho một cái gì đó như thế?
Zifre

Boost.Variant hoàn toàn an toàn.
user21714

1
Rất tiếc, có, nó là loại an toàn, nhưng nó thực hiện gõ thời gian chạy.
Zifre

5
Tôi không thấy Tuple có thể được thay thế Any / Variant như thế nào. Họ không làm điều tương tự.
Mankude

1
@Zifre Tôi có thể nói chuyện với tác giả, nhưng tôi nghĩ hàm ý ở đây là sử dụng chúng cùng với các loại container khác.
Tim Seguine
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.