Существует множество способов вернуть несколько параметров. Я собираюсь быть внимательным.
Используйте опорные параметры:
void foo( int& result, int& other_result );
использовать параметры указателя:
void foo( int* result, int* other_result );
что имеет преимущество в том, что вы должны сделать &
вызов на сайте вызова, возможно, предупредив людей, что это выходной параметр.
Напишите шаблон и используйте его:
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)...); }
};
тогда мы можем сделать:
void foo( out<int> result, out<int> other_result )
и все хорошо. foo
больше не может читать любые значения, переданные в качестве бонуса.
Другие способы определения места, в которое вы можете поместить данные, могут быть использованы для построения out
. Например, обратный вызов для размещения чего-либо.
Мы можем вернуть структуру:
struct foo_r { int result; int other_result; };
foo_r foo();
Whick работает нормально в каждой версии C ++, и в C ++ 17 это также позволяет:
auto&&[result, other_result]=foo();
по нулевой цене. Параметры даже не могут быть перемещены благодаря гарантированному выбору.
Мы могли бы вернуть std::tuple
:
std::tuple<int, int> foo();
недостаток в том, что параметры не названы. Это позволяетC ++ 17:
auto&&[result, other_result]=foo();
также. ДоC ++ 17 вместо этого мы можем сделать:
int result, other_result;
std::tie(result, other_result) = foo();
что немного более неловко. Однако здесь гарантированное разрешение не работает.
Зайдя на чужую территорию (а это после out<>
!), Мы можем использовать стиль продолжения:
void foo( std::function<void(int result, int other_result)> );
и теперь звонящие делают:
foo( [&](int result, int other_result) {
/* code */
} );
Преимущество этого стиля в том, что вы можете возвращать произвольное количество значений (с единообразным типом) без необходимости управления памятью:
void get_all_values( std::function<void(int)> value )
value
обратный вызов можно назвать 500 раз , когда вы get_all_values( [&](int value){} )
.
Для чистого безумия вы можете даже использовать продолжение в продолжении.
void foo( std::function<void(int, std::function<void(int)>)> result );
чье использование выглядит так:
foo( [&](int result, auto&& other){ other([&](int other){
/* code */
}) });
что позволило бы много-один отношения между result
и other
.
Опять же со значениями Uniforn, мы можем сделать это:
void foo( std::function< void(span<int>) > results )
здесь мы вызываем обратный вызов с диапазоном результатов. Мы даже можем сделать это неоднократно.
Используя это, вы можете иметь функцию, которая эффективно передает мегабайты данных без какого-либо выделения из стека.
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
}
Теперь std::function
это немного тяжело, так как мы будем делать это в средах без выделения ресурсов с нулевыми накладными расходами. Поэтому мы бы хотели, чтобы это function_view
никогда не выделялось.
Другое решение:
std::function<void(std::function<void(int result, int other_result)>)> foo(int input);
где вместо того, чтобы принимать обратный вызов и вызывать его, foo
вместо этого возвращается функция, которая принимает обратный вызов.
foo (7) ([&] (int result, int other_result) {/ * code * /}); это отделяет выходные параметры от входных параметров, используя отдельные скобки.
С variant
иC ++ 20сопрограммы, вы могли бы сделать foo
генератор варианта возвращаемых типов (или просто возвращаемый тип). Синтаксис еще не установлен, поэтому я не буду приводить примеры.
В мире сигналов и слотов, функция, которая выставляет набор сигналов:
template<class...Args>
struct broadcaster;
broadcaster<int, int> foo();
позволяет создать foo
асинхронную работу, которая транслирует результат после его завершения.
Вниз по этой линии у нас есть множество методов конвейерной обработки, когда функция не делает что-то, а скорее организует подключение данных каким-либо образом, а выполнение относительно независимо.
foo( int_source )( int_dest1, int_dest2 );
тогда этот код ничего не делает, пока int_source
не предоставит целые числа для его предоставления. Когда это произойдет, int_dest1
и int_dest2
начните получать результаты.