有很多方法可以返回多个参数。我会很高兴的。
使用参考参数:
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结果,int other_result){/ *代码* /}); 这通过使用单独的括号将输出参数与输入参数分开。
与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
开始接收结果。