Thực hiện các toán tử so sánh thông qua 'tuple' và 'tie', một ý tưởng hay?


98

(Lưu ý: tupletiecó thể được lấy từ Boost hoặc C ++ 11.)
Khi viết cấu trúc nhỏ chỉ có hai phần tử, đôi khi tôi có xu hướng chọn a std::pair, vì tất cả những thứ quan trọng đã được thực hiện cho kiểu dữ liệu đó, như operator<đối với thứ tự nghiêm ngặt-yếu .
Tuy nhiên, nhược điểm là các tên biến vô dụng. Ngay cả khi chính tôi đã tạo ra nó typedef, tôi sẽ không nhớ 2 ngày sau đó là gì firstsecondchính xác là gì , đặc biệt nếu cả hai đều thuộc cùng một loại. Điều này thậm chí còn tồi tệ hơn đối với nhiều hơn hai thành viên, vì việc lồng vào nhau pairsẽ rất tệ.
Tùy chọn khác cho điều đó làtuple, từ Boost hoặc C ++ 11, nhưng điều đó không thực sự trông đẹp và rõ ràng hơn. Vì vậy, tôi quay lại việc tự viết cấu trúc, bao gồm bất kỳ toán tử so sánh nào cần thiết.
Vì đặc biệt là operator<có thể khá cồng kềnh, tôi đã nghĩ đến việc giải quyết toàn bộ mớ hỗn độn này bằng cách chỉ dựa vào các phép toán được xác định cho tuple:

Ví dụ về operator<, ví dụ cho thứ tự nghiêm ngặt-yếu:

bool operator<(MyStruct const& lhs, MyStruct const& rhs){
  return std::tie(lhs.one_member, lhs.another, lhs.yet_more) <
         std::tie(rhs.one_member, rhs.another, rhs.yet_more);
}

( tieLàm cho một tuplesố T&tài liệu tham khảo từ các đối số được thông qua.)


Chỉnh sửa : Đề xuất từ ​​@DeadMG để kế thừa riêng tư tuplekhông phải là một đề xuất xấu, nhưng nó có một số nhược điểm:

  • Nếu các nhà khai thác là tự do (có thể là bạn bè), tôi cần kế thừa công khai
  • Với truyền, các hàm / toán tử của tôi ( operator=cụ thể) có thể dễ dàng bị bỏ qua
  • Với tiegiải pháp, tôi có thể loại bỏ một số thành viên nếu họ không quan trọng đối với việc đặt hàng

Có bất kỳ hạn chế nào trong việc triển khai này mà tôi cần xem xét không?


1
Có vẻ hoàn toàn hợp lý với tôi ...
ildjarn

1
Đó là một ý tưởng rất thông minh, ngay cả khi nó không thành công. Tôi sẽ phải điều tra điều này.
templatetypedef

Điều này trông khá hợp lý. Cạm bẫy duy nhất tôi có thể nghĩ đến bây giờ là tiekhông thể áp dụng cho các thành viên trường bit.
Ise Wisteria

4
Tôi thích ý tưởng này! Nếu các tie(...)cuộc gọi sẽ bị trùng lặp trong các toán tử khác nhau (=, ==, <, v.v.), bạn có thể viết một phương thức nội tuyến riêng make_tuple(...)để đóng gói nó và sau đó gọi nó từ nhiều nơi khác, như trong return lhs.make_tuple() < rhs.make_tuple();(mặc dù kiểu trả về từ phương pháp đó có thể thú vị để khai báo!)
aldo

13
@aldo: C ++ 14 giải cứu! auto tied() const{ return std::tie(the, members, here); }
Xeo

Câu trả lời:


60

Điều này chắc chắn sẽ giúp bạn viết một toán tử chính xác dễ dàng hơn là tự lăn nó. Tôi muốn nói rằng chỉ xem xét một cách tiếp cận khác nếu việc lập hồ sơ cho thấy thao tác so sánh là một phần tốn thời gian trong ứng dụng của bạn. Nếu không, việc dễ dàng duy trì điều này sẽ vượt trội hơn bất kỳ mối quan tâm nào về hiệu suất.


17
Tôi không thể tưởng tượng một trường hợp tuple<>'s operator<sẽ chậm hơn so với bất kỳ một tay.
ildjarn

51
Tôi đã có lần chính xác ý tưởng này và đã thực hiện một số thử nghiệm. Thực sự ngạc nhiên khi thấy rằng trình biên dịch đã sắp xếp và tối ưu hóa mọi thứ liên quan đến các bộ giá trị và tham chiếu, tạo ra sự lắp ráp gần giống với mã viết tay.
JohannesD

7
@JohannesD: Tôi có thể ủng hộ lời khai đó, cũng đã làm như vậy một lần
xem

Điều này có đảm bảo đặt hàng yếu nghiêm ngặt không? Làm sao?
CinCout

5

Tôi đã gặp phải vấn đề tương tự này và giải pháp của tôi sử dụng các mẫu khác nhau c ++ 11. Đây là mã:

Phần .h:

/***
 * Generic lexicographical less than comparator written with variadic templates
 * Usage:
 *   pass a list of arguments with the same type pair-wise, for intance
 *   lexiLessthan(3, 4, true, false, "hello", "world");
 */
bool lexiLessthan();

template<typename T, typename... Args>
bool lexiLessthan(const T &first, const T &second, Args... rest)
{
  if (first != second)
  {
    return first < second;
  }
  else
  {
    return lexiLessthan(rest...);
  }
}

Và .cpp cho trường hợp cơ sở không có đối số:

bool lexiLessthan()
{
  return false;
}

Bây giờ ví dụ của bạn trở thành:

return lexiLessthan(
    lhs.one_member, rhs.one_member, 
    lhs.another, rhs.another, 
    lhs.yet_more, rhs.yet_more
);

Tôi đặt giải pháp tương tự ở đây nhưng không yêu cầu toán tử! =. stackoverflow.com/questions/11312448/...
steviekm3

3

Theo ý kiến ​​của tôi, bạn vẫn chưa giải quyết được vấn đề giống như các giải std::tuplepháp - cụ thể là, bạn phải biết cả số lượng và tên của mỗi biến thành viên, bạn đang nhân bản nó hai lần trong hàm. Bạn có thể chọn privatethừa kế.

struct somestruct : private std::tuple<...> {
    T& GetSomeVariable() { ... }
    // etc
};

Cách tiếp cận này hơi rắc rối hơn một chút để bắt đầu, nhưng bạn chỉ duy trì các biến và tên ở một nơi, thay vì ở mọi nơi cho mọi toán tử bạn muốn quá tải.


3
Vì vậy, tôi sẽ sử dụng các trình truy cập có tên cho các biến như T& one_member(){ return std::get<0>(*this); }vv? Nhưng điều đó có cần tôi cung cấp một phương thức như vậy cho mỗi "thành viên" mà tôi có, bao gồm các quá tải cho phiên bản const và không phải const?
Xeo

@Xeo Tôi không thấy trình truy cập được đặt tên yêu cầu bất kỳ công việc nào hơn là tạo các biến thực tế. Dù bằng cách nào thì bạn cũng phải có một tên riêng cho từng biến. Tôi cho rằng sẽ có sự trùng lặp đối với const / không phải const. Tuy nhiên, bạn có thể làm mẫu cho tất cả công việc này.
Lee Louviere

1

Nếu bạn định sử dụng nhiều hơn một toán tử quá tải hoặc nhiều phương thức từ tuple, tôi khuyên bạn nên đặt tuple thành một thành viên của lớp hoặc dẫn xuất từ ​​tuple. Nếu không, những gì bạn đang làm còn nhiều việc hơn. Khi quyết định giữa cả hai, một câu hỏi quan trọng cần trả lời là: Bạn có muốn lớp học của mình trở thành một nhóm không? Nếu không, tôi khuyên bạn nên chứa một tuple và giới hạn giao diện bằng cách sử dụng ủy quyền.

Bạn có thể tạo người truy cập để "đổi tên" các thành viên của tuple.


Tôi đọc câu hỏi của OP có nghĩa là " operator<sử dụng lớp học của tôi std::tiecó hợp lý không?" Tôi không hiểu câu trả lời này liên quan đến câu hỏi đó như thế nào.
ildjarn

@ildjarn Có một số nhận xét mà tôi không đăng ở đây. Tôi đã biên soạn mọi thứ để nó đọc tốt hơn.
Lee Louviere
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.