Có một loạt các cách để trả về nhiều tham số. Tôi sẽ rất phấn khởi.
Sử dụng tham số tham chiếu:
void foo( int& result, int& other_result );
sử dụng tham số con trỏ:
void foo( int* result, int* other_result );
có lợi thế là bạn phải thực hiện &
tại trang web cuộc gọi, có thể cảnh báo mọi người rằng đó là một tham số ngoài.
Viết một mẫu và sử dụng nó:
template<class T>
struct out {
std::function<void(T)> target;
out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(std::move(in)); }) {}
out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
template<class...Args> // TODO: SFINAE enable_if test
void emplace(Args&&...args) {
target( T(std::forward<Args>(args)...) );
}
template<class X> // TODO: SFINAE enable_if test
void operator=(X&&x){ emplace(std::forward<X>(x)); }
template<class...Args> // TODO: SFINAE enable_if test
void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};
sau đó chúng ta có thể làm:
void foo( out<int> result, out<int> other_result )
và tất cả đều tốt foo
không còn có thể đọc bất kỳ giá trị nào được chuyển vào dưới dạng phần thưởng.
Các cách khác để xác định một vị trí bạn có thể đặt dữ liệu có thể được sử dụng để xây dựng out
. Một cuộc gọi lại để đặt những thứ ở đâu đó, ví dụ.
Chúng ta có thể trả về một cấu trúc:
struct foo_r { int result; int other_result; };
foo_r foo();
Whick hoạt động tốt trong mọi phiên bản của C ++ và trong c ++ 17 điều này cũng cho phép:
auto&&[result, other_result]=foo();
với chi phí bằng không. Các thông số thậm chí không thể được di chuyển nhờ cuộc bầu cử được đảm bảo.
Chúng tôi có thể trả lại một std::tuple
:
std::tuple<int, int> foo();
trong đó có nhược điểm là các tham số không được đặt tên. Điều này cho phépc ++ 17:
auto&&[result, other_result]=foo();
cũng. Trướcc ++ 17 thay vào đó chúng ta có thể làm:
int result, other_result;
std::tie(result, other_result) = foo();
mà chỉ là một chút vụng về Tuy nhiên, cuộc bầu cử được đảm bảo không hoạt động ở đây.
Đi vào lãnh thổ xa lạ (và đây là sau out<>
!), Chúng ta có thể sử dụng kiểu tiếp tục đi qua:
void foo( std::function<void(int result, int other_result)> );
và bây giờ người gọi làm:
foo( [&](int result, int other_result) {
/* code */
} );
một lợi ích của kiểu này là bạn có thể trả về một số giá trị tùy ý (với loại thống nhất) mà không phải quản lý bộ nhớ:
void get_all_values( std::function<void(int)> value )
cuộc value
gọi lại có thể được gọi 500 lần khi bạn get_all_values( [&](int value){} )
.
Đối với chứng điên cuồng thuần túy, bạn thậm chí có thể sử dụng phần tiếp theo cho phần tiếp theo.
void foo( std::function<void(int, std::function<void(int)>)> result );
có công dụng như:
foo( [&](int result, auto&& other){ other([&](int other){
/* code */
}) });
điều này sẽ cho phép nhiều mối quan hệ giữa một result
và other
.
Một lần nữa với các giá trị của California, chúng ta có thể làm điều này:
void foo( std::function< void(span<int>) > results )
ở đây, chúng tôi gọi lại cuộc gọi với một loạt các kết quả. Chúng tôi thậm chí có thể làm điều này nhiều lần.
Sử dụng điều này, bạn có thể có một chức năng truyền hiệu quả megabyte dữ liệu mà không cần thực hiện bất kỳ phân bổ nào ngoài ngăn xếp.
void foo( std::function< void(span<int>) > results ) {
int local_buffer[1024];
std::size_t used = 0;
auto send_data=[&]{
if (!used) return;
results({ local_buffer, used });
used = 0;
};
auto add_datum=[&](int x){
local_buffer[used] = x;
++used;
if (used == 1024) send_data();
};
auto add_data=[&](gsl::span<int const> xs) {
for (auto x:xs) add_datum(x);
};
for (int i = 0; i < 7+(1<<20); ++i) {
add_datum(i);
}
send_data(); // any leftover
}
Bây giờ, std::function
hơi nặng nề cho việc này, vì chúng ta sẽ thực hiện việc này trong môi trường không phân bổ không chi phí. Vì vậy, chúng tôi muốn một function_view
cái không bao giờ phân bổ.
Một giải pháp khác là:
std::function<void(std::function<void(int result, int other_result)>)> foo(int input);
trong đó thay vì thực hiện cuộc gọi lại và gọi nó, foo
thay vào đó sẽ trả về một hàm nhận cuộc gọi lại.
foo (7) ([&] (int result, int other_result) {/ * code * /}); điều này phá vỡ các tham số đầu ra từ các tham số đầu vào bằng cách có dấu ngoặc riêng.
Với variant
vàc ++ 20coroutines, bạn có thể tạo ra foo
một biến thể của các kiểu trả về (hoặc chỉ là kiểu trả về). Cú pháp chưa được sửa, vì vậy tôi sẽ không đưa ra ví dụ.
Trong thế giới của tín hiệu và khe, một chức năng hiển thị một tập hợp các tín hiệu:
template<class...Args>
struct broadcaster;
broadcaster<int, int> foo();
cho phép bạn tạo một foo
hoạt động không đồng bộ và phát kết quả khi kết thúc.
Ở dòng này, chúng ta có nhiều kỹ thuật đường ống, trong đó một chức năng không làm gì đó mà chỉ sắp xếp để dữ liệu được kết nối theo một cách nào đó và việc thực hiện tương đối độc lập.
foo( int_source )( int_dest1, int_dest2 );
sau đó mã này không làm gì cả cho đến khi int_source
có số nguyên để cung cấp nó. Khi nó làm, int_dest1
và int_dest2
bắt đầu nhận kết quả.