从C+返回多个值+;功能 是否有一种从C++函数返回多个值的首选方法?例如,假设一个函数将两个整数相除,并返回商和余数。我通常看到的一种方法是使用参考参数: void divide(int dividend, int divisor, int& quotient, int& remainder); void foo( int& result, int& other_result ); void foo( int* result, int* other_result );
一种变体是返回一个值并通过参考参数传递另一个值:从C+返回多个值+;功能 是否有一种从C++函数返回多个值的首选方法?例如,假设一个函数将两个整数相除,并返回商和余数。我通常看到的一种方法是使用参考参数: void divide(int dividend, int divisor, int& quotient, int& remainder); void foo( int& result, int& other_result ); void foo( int* result, int* other_result );,c++,c++17,c++20,C++,C++17,C++20,一种变体是返回一个值并通过参考参数传递另一个值: int divide(int dividend, int divisor, int& remainder); 另一种方法是声明一个包含所有结果的结构,并返回: struct divide_result { int quotient; int remainder; }; divide_result divide(int dividend, int divisor); 这些方法中有一种是普遍首选的,还是有其他建议 编辑:
int divide(int dividend, int divisor, int& remainder);
另一种方法是声明一个包含所有结果的结构,并返回:
struct divide_result {
int quotient;
int remainder;
};
divide_result divide(int dividend, int divisor);
这些方法中有一种是普遍首选的,还是有其他建议
编辑:在现实世界的代码中,可能有两个以上的结果。它们也可能是不同的类型。备选方案包括数组和,但在这里没有一个是合适的 一些(如历史上的Win32中的Microsoft)倾向于使用参考参数以简化操作,因为很清楚谁分配了资源以及它在堆栈上的外观,减少了结构的扩散,并允许为成功使用单独的返回值
“纯”程序员更喜欢struct,假设它是函数值(这里就是这种情况),而不是函数偶然触及的东西。如果您有一个更复杂的过程,或者有状态,您可能会使用引用(假设您有理由不使用类)。这完全取决于实际函数和多个值的含义,以及它们的大小:
- 如果它们像分数示例中那样相关,那么我将使用结构或类实例
- 如果它们不是真正相关的,并且不能分组到一个类/结构中,那么也许您应该将方法重构为两个
- 根据要返回的值在内存中的大小,您可能希望返回指向类实例或结构的指针,或者使用引用参数
std::pair
(通常为typedef'd)。对于两个以上的返回结果,您应该查看boost::tuple
(在C++11及更新版本中,有std::tuple
)
< P>在C++ 17中引入结构化绑定,返回<代码> STD::tuple < /代码>应该成为公认的标准。< /P> <代码> STD::配对除法(int红利,int除数)
std::pair<int, int> divide(int dividend, int divisor)
{
// :
return std::make_pair(quotient, remainder);
}
std::pair<int, int> answer = divide(5,2);
// answer.first == quotient
// answer.second == remainder
{
// :
return std::make_pair(商,余数);
}
标准::配对答案=除法(5,2);
//答案.第一个==商
//answer.second==余数
std::pair本质上是您的结构解决方案,但已经为您定义了,并且可以适应任何两种数据类型 我倾向于在这样的函数中使用out-vals,因为我坚持函数返回成功/错误代码的范例,并且我喜欢保持一致 我想说没有首选方法,这完全取决于您将如何处理响应。如果结果将在进一步的处理中一起使用,那么结构是有意义的,如果不是,我倾向于将其作为单独的引用传递,除非函数将在复合语句中使用:
x=divide(x,y,z)+divide(a,b,c)代码>
我经常选择在参数列表中通过引用传递“out structures”,而不是通过复制来传递返回新结构的开销(但这会让小事情变得棘手)
void divide(整数除数、整数除数、Answer&ans)
我们的参数令人困惑吗?作为引用发送的参数表示该值将要更改(与常量引用相反)。合理的命名也可以消除混淆。对于从函数返回多个值的通用系统,Boost tuple是我的首选
可能的例子:
include "boost/tuple/tuple.hpp"
tuple <int,int> divide( int dividend,int divisor )
{
return make_tuple(dividend / divisor,dividend % divisor )
}
包括“boost/tuple/tuple.hpp”
元组除法(整数除数,整数除数)
{
返回生成元组(股息/除数,股息%除数)
}
如果您的函数通过引用返回值,编译器在调用其他函数时无法将其存储在寄存器中,因为理论上,第一个函数可以将传递给它的变量的地址保存在全局可访问变量中,并且任何随后调用的函数都可能更改它,因此编译器将具有(1)在调用其他函数之前,将寄存器中的值保存回内存,(2)在任何此类调用之后需要时,再次从内存中读取该值
如果您通过引用返回,您的程序优化将受到影响。就个人而言,我通常不喜欢返回参数,原因有很多:
- 在调用中,哪些参数是in,哪些参数是OUT并不总是很明显
- 您通常必须创建一个局部变量来捕获结果,而返回值可以内联使用(这可能是个好主意,也可能不是,但至少您可以选择)
- 对我来说,一个函数有一个“门内”和一个“门外”似乎更干净——所有的输入都在这里,所有的输出都在那里
- 我喜欢让我的论点列表尽可能简短
我对pair/tuple技术也有一些保留。主要是,返回值通常没有自然顺序。代码的读者如何知道result.first是商还是余数?实现者可以更改顺序,这将破坏现有代码。如果值是相同的类型,因此不会生成编译器错误或警告,则这种情况尤其危险。实际上,这些参数也适用于返回参数
下面是另一个代码示例,这一个稍微简单一些:
pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth,
double planeAirspeed, double planeCourse);
pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.first << endl;
cout << result.second << endl;
成对计算结果速度(双风速、双风向角、,
双机空速、双机航向);
配对结果=计算结果速度(25320280,90);
在C(因此也是C++)标准中,使用div
,ldiv
(在C99中,lldiv
)函数从
(或
)返回结构是有先例的
“混合”
#include <tuple>
std::tuple<int, int> divide(int dividend, int divisor) {
return {dividend / divisor, dividend % divisor};
}
#include <iostream>
int main() {
using namespace std;
auto [quotient, remainder] = divide(14, 3);
cout << quotient << ',' << remainder << endl;
}
auto divide(int dividend, int divisor) {
struct result {int quotient; int remainder;};
return result {dividend / divisor, dividend % divisor};
}
#include <iostream>
int main() {
using namespace std;
auto result = divide(14, 3);
cout << result.quotient << ',' << result.remainder << endl;
// or
auto [quotient, remainder] = divide(14, 3);
cout << quotient << ',' << remainder << endl;
}
class div{
public:
int remainder;
int quotient(int dividend, int divisor){
remainder = ...;
return ...;
}
};
# include <tuple>
# include <iostream>
using namespace std;
tuple < int,int,int,int,int > cal(int n1, int n2)
{
return make_tuple(n1/n2,n1%n2,n1+n2,n1-n2,n1*n2);
}
int main()
{
int qut,rer,add,sub,mul,a,b;
cin>>a>>b;
tie(qut,rer,add,sub,mul)=cal(a,b);
cout << "quotient= "<<qut<<endl;
cout << "remainder= "<<rer<<endl;
cout << "addition= "<<add<<endl;
cout << "subtraction= "<<sub<<endl;
cout << "multiplication= "<<mul<<endl;
return 0;
}
template<typename T1,typename T2,typename T3>
struct many {
T1 a;
T2 b;
T3 c;
};
// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;
auto f(){ return many{string(),5.7, unmovable()}; };
int main(){
// in place construct x,y,z with a string, 5.7 and unmovable.
auto [x,y,z] = f();
}
int divide(int a,int b,int quo,int &rem)
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 )
struct foo_r { int result; int other_result; };
foo_r foo();
auto&&[result, other_result]=foo();
std::tuple<int, int> foo();
auto&&[result, other_result]=foo();
int result, other_result;
std::tie(result, other_result) = foo();
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 )
void foo( std::function<void(int, std::function<void(int)>)> result );
foo( [&](int result, auto&& other){ other([&](int other){
/* code */
}) });
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<void(std::function<void(int result, int other_result)>)> foo(int input);
foo(7)([&](int result, int other_result){ /* code */ });
template<class...Args>
struct broadcaster;
broadcaster<int, int> foo();
foo( int_source )( int_dest1, int_dest2 );
static struct SomeReturnType {int a,b,c; string str;} SomeFunction()
{
return {1,2,3,string("hello world")}; // make sure you return values in the right order!
}
SomeReturnType st = SomeFunction();
cout << "a " << st.a << endl;
cout << "b " << st.b << endl;
cout << "c " << st.c << endl;
cout << "str " << st.str << endl;
#include <iostream>
using namespace std;
// different values of [operate] can return different number.
int yourFunction(int a, int b, int operate)
{
a = 1;
b = 2;
if (operate== 1)
{
return a;
}
else
{
return b;
}
}
int main()
{
int a, b;
a = yourFunction(a, b, 1); // get return 1
b = yourFunction(a, b, 2); // get return 2
return 0;
}