在C++里,函数声明只能写一个返回类型,这并不代表业务上只能产出一个结果。实际开发中,我们经常需要同时拿到状态码、计算结果以及提示信息。语言层面提供了多种机制来绕过单返回值的限制,不同写法在可读性、性能和适用场景上差别明显。

使用引用或指针参数作为输出参数
最传统也最直观的办法,是把需要返回的值通过函数参数以引用或指针形式传进去,在函数体内修改它们。这种方式在C语言时代就很常见,优点是没有任何额外对象构造开销,所有结果都直接写在调用者提供的变量上。
下面这段代码演示了用引用参数返回除法的结果和是否成功。调用者先定义变量,再传进去,函数通过修改引用来完成多值传出。它的接口虽然能工作,但语义不够清晰:看函数签名时,很难一眼分清哪个是入参、哪个是出参。
#include <iostream>
bool divide(int a, int b, int& out_result, int& out_remain) {
if (b == 0) return false;
out_result = a / b;
out_remain = a % b;
return true;
}
int main() {
int res = 0, rem = 0;
if (divide(10, 3, res, rem)) {
std::cout << res << " " << rem << std::endl;
}
return 0;
}
这种写法的缺点是容易引发顺序错误。如果调用时把两个引用位置写反,编译器不会报错,但逻辑已经错了。另外,当输出参数变多,函数参数列表会很长,维护成本上升。在性能敏感且类型简单的底层模块里,它依然有存在价值,但在上层业务逻辑中不够友好。
利用std::pair与std::tuple返回复合值
C++标准库提供了<utility>里的std::pair和<tuple>里的std::tuple,可以把多个值打包成一个对象返回。pair适合两个值,tuple适合两个及以上。它们都属于值类型,支持移动语义,在现代编译器中开销极小。
下面的例子用std::tuple返回商、余数和是否成功三个值。函数内部用std::make_tuple构造,调用者可以用std::get按索引取值。这种方式让函数签名明确表达了返回内容,不需要额外定义结构体。
#include <iostream>
#include <tuple>
std::tuple<int, int, bool> divide_tuple(int a, int b) {
if (b == 0) return std::make_tuple(0, 0, false);
return std::make_tuple(a / b, a % b, true);
}
int main() {
auto t = divide_tuple(10, 3);
if (std::get<2>(t)) {
std::cout << std::get<0>(t) << " " << std::get<1>(t) << std::endl;
}
return 0;
}
直接用std::get索引取值的弊端是可读性差,索引数字没有语义。C++17引入的结构化绑定解决了这个问题,可以用auto [res, rem, ok] = divide_tuple(10, 3);把元组拆成有名字的变量。需要注意的是,结构化绑定要求编译器支持C++17,且元组元素类型在编译期确定。相比引用出参,tuple方案把多返回值变成真正的返回值,调用处更干净。
定义结构体或使用std::tie接收多值
当返回的数据有明确业务含义,定义一个轻量结构体是最易维护的方式。结构体成员有名字,调用者通过点号访问,比tuple索引更不容易出错。如果项目受限于旧标准不能用结构化绑定,还可以用std::tie把tuple解开赋给已有变量。
下面展示结构体方案和std::tie接收方案。结构体DivResult清晰表达了三个字段,std::tie则允许把tuple的值绑到已定义的变量上,避免重复构造。
#include <iostream>
#include <tuple>
struct DivResult {
int quotient;
int remainder;
bool success;
};
DivResult divide_struct(int a, int b) {
if (b == 0) return {0, 0, false};
return {a / b, a % b, true};
}
int main() {
DivResult r = divide_struct(10, 3);
if (r.success) {
std::cout << r.quotient << " " << r.remainder << std::endl;
}
int q, rem;
bool ok;
std::tie(q, rem, ok) = divide_struct(10, 3);
if (ok) {
std::cout << q << " " << rem << std::endl;
}
return 0;
}
结构体方案在返回字段超过三个、或字段会被多处复用时优势明显。它让接口自解释,也方便后续增加字段而不破坏调用点。std::tie适合临时兼容老代码,新项目更推荐结构体加结构化绑定。综合来看,C++指定函数返回多个值并不是语法限制,而是选型问题:底层用引用出参,通用逻辑用tuple,稳定业务用结构体,能显著提升代码质量。