2010-08-07 22 views
11

tuple khi tăng và TR1/C++ 0x cung cấp phương thức thuận tiện (cho người viết hàm) trả về hai giá trị từ hàm - tuy nhiên dường như thiệt hại một đặc điểm quan trọng của ngôn ngữ cho người gọi: khả năng chỉ đơn giản là sử dụng chức năng để khởi tạo một biến:Cách khởi tạo với nhiều giá trị trả lại bằng C++ (0x)

T happy(); 
const auto meaningful_name(happy()); // RVO means no excess copies 

nhưng đối với:

tuple<T,U> sad(); 

chúng tôi hoặc đã từ bỏ các khả năng để chọn một tên có ý nghĩa cho giá trị trả lại của chúng tôi và sử dụng get<n>() ở khắp mọi nơi:

const auto two_unrelated_things(sad()); 

hoặc thực hiện một tạm thời:

const auto unwanted_named_temporary(sad()); 
const auto one_name(get<0>(unwanted_named_temporary)); 
const auto two_name(get<1>(unwanted_named_temporary)); 

hoặc chuyển từ khởi sự phân công, mà chỉ hoạt động khi các loại được chuyển nhượng, và phá vỡ auto:

tuple_element<0, decltype(sad())>::type one_mutable; // there might be a less 
tuple_element<1, decltype(sad())>::type two_mutable; // verbose way 
tie(one_mutable,two_mutable) = sad(); 

hoặc làm điều gì đó không tự nhiên với một lớp học địa phương:

const struct ugh { 
    ugh(decltype(sad()) rhs) : one_name(get<0>(rhs)), two_name(get<1>(rhs)) {} 
    const tuple_element<0, decltype(sad())>::type one_name; 
    const tuple_element<1, decltype(sad())>::type two_name; 
} stuff(sad()); // at least we avoid the temporary and get initialization 

Có cách nào tốt hơn không? Tôi đang sử dụng VC10 tương thích xây dựng ở trên, sẽ bất cứ điều gì trong đầy đủ c + + 0x hoặc tăng giúp đỡ?

Lý tưởng nhất là nó sẽ:

  • cho phép tôi sử dụng khởi tạo, không chỉ là nhiệm vụ
  • để cho gọi chọn tên cho trở-thành biến
  • không làm bản sao thêm
  • làm việc cho cả biến ngăn xếp và thành viên lớp học
  • có thể là thư viện mẫu lớn, nhưng có cú pháp sane cho người gọi và người viết hàm
+2

Câu hỏi thú vị, mặc dù tôi không thấy cách bạn có thể xác định các biến của các loại khác nhau trong một biểu thức duy nhất. - Tôi nghĩ rằng "hoặc làm cho một tạm thời" tùy chọn có thể là OK, nếu bạn thay đổi các biến được đặt tên thành tài liệu tham khảo (tránh sao chép). – UncleBens

+0

Điểm tốt về tài liệu tham khảo - Tôi nghĩ đó là giải pháp cho các biến ngăn xếp. tôi đã cố gắng làm như vậy trong một lớp học: class C { công cộng: C() sr (buồn()), một (lấy <0> (sr)), hai (được <1> (sr)) {} const T & one; const U & two; riêng tư: tuple sr; Nhưng nó trông giống như trong VC10, C là hai con trỏ lớn hơn tuple, không phải là một thỏa thuận lớn nhưng loại lame - nó sẽ không hợp pháp cho trình biên dịch để nhận ra rằng các tài liệu tham khảo là bí danh và không phân bổ không gian trong trường hợp cho họ? Đó không phải là lý do tại sao con trỏ tham khảo là bất hợp pháp ngay từ đầu? – BCoates

+0

Với một lớp, nếu dữ liệu được lưu trữ như một bộ dữ liệu, bạn chỉ có thể cung cấp các phương thức accessor có tên, gọi phương thức 'get ' tương ứng. Tôi nghi ngờ rằng sẽ có một giải pháp dựa trên "mẫu điên", bởi vì ngôn ngữ cốt lõi đơn giản dường như không hỗ trợ những gì bạn đang yêu cầu. Có lẽ bạn chỉ có thể giảm số lượng ký tự mà bạn phải nhập bằng các macro ... – UncleBens

Trả lời

2
std::tuple<Type1, Type2> returnValue = sad(); 
Type1& first = std::get<0>(returnValue); 
Type2& second = std::get<1>(returnValue); 

Tôi không chắc chắn đạn thứ tư của bạn là gì, nhưng thỏa mãn tất cả những gì còn lại.

* chỉnh sửa: Dựa trên nhận xét của bạn ở trên, tôi đã tìm ra ý nghĩa của bạn bằng dấu đầu dòng thứ tư.

struct Object { 
    Object(const std::tuple<Type1, Type2>& t) : value(t) { } 
    Type1& First() { return std::get<0>(value); } 
    Type2& second() { return std::get<1>(value); } 
private: 
    std::tuple<Type1, Type2> value; 
} 

Sửa đổi khi cần.

Bạn cũng có thể không sử dụng std::tuple nếu tất cả các giá trị trả về không liên quan đến nhau mà bạn phải chia chúng để chúng được sử dụng hợp lý. Mọi người đã nhận được trong nhiều năm trở lại struct s với các trường được đặt tên hợp lý hoặc bằng cách chấp nhận tham số tham chiếu cho đầu ra.

Là một sang một bên, bạn dường như đang yêu auto. Đừng thế. Đó là một tính năng tuyệt vời, nhưng đây là không phải là cách sử dụng. Mã của bạn sẽ kết thúc không đọc được nếu bạn không chỉ định các loại theo thời gian.

+0

Khi tôi nghĩ về bit "thành viên của lớp", đây có phải là tình huống mà bạn có thể bỏ qua tất cả các bản sao không? Tôi nghĩ rằng bạn sẽ kết thúc sao chép 'giá trị' ít nhất một lần trong mọi trường hợp, mà rõ ràng sẽ yêu cầu sao chép các thành viên của nó ít nhất một lần. Vì vậy, bạn có lẽ cũng có thể chỉ cần lưu trữ các giá trị trực tiếp và quên lưu trữ tuple ở tất cả. –

+0

Tôi đồng ý không sử dụng 'std :: tuple' khi nó không thích hợp - Tôi chỉ lo lắng rằng nó sẽ trở thành thành ngữ và làm cho code ít dễ hiểu hơn và có được' get (sometuple) ' Điều đó nói rằng , có vẻ như tôi lo lắng quá nhiều về các bản sao phụ: 'tie (a, b) = sad()' trong một hàm tạo thành công elides tất cả các bản sao của các thành viên tuple khi sad() trả về 'make_tuple (...)' . Có lẽ bài học là "' const' thành viên dữ liệu là một rắc rối ". – BCoates

+1

@ bcoates: Về việc lạm dụng và lạm dụng bộ dữ liệu. Thư viện chuẩn là một chút có lỗi khi nó đại diện cho các dãy là 'cặp >. OTOH, boost đang sử dụng một thay thế cao cấp: 'iterator_range ' với phương thức 'begin()' và 'end()'. Một phạm vi không phải là một cặp trừu tượng, 'đầu tiên' và' giây' có ý nghĩa cụ thể! - Tôi không nghĩ người ta nên rất vui vẻ trong mã không chung chung. – UncleBens

Các vấn đề liên quan