最直接方式是用非const引用参数修改原始变量;其次可用std::tuple打包返回;语义明确时推荐自定义结构体;避免用std::pair承载超两个值,注意生命周期与移动语义。

用引用参数修改原始变量
这是最直接、最常用的方式:把需要返回的变量以非 const 引用传入,函数内部直接赋值。调用方看到的是原变量被改写,等效于“返回多个值”。
常见错误是传值或传 const 引用——那样函数里改的只是副本,调用方完全看不到变化。
-
void parse_point(const std::string& s, double& x, double& y)是合理签名;double x, y;调用前定义,进函数就被写入 - 不能写成
void parse_point(..., double x, double y)(传值)或double& const x(语法错) - 引用方式零拷贝、无构造开销,适合返回基础类型或已存在的大对象(如
std::vector)
用 std::tuple 一次性打包返回
当多个结果逻辑上属于同一组、且调用方愿意接收一个聚合体时,std::tuple 比引用更清晰——它显式表达了“这仨是一起算出来的”。C++17 后还能用结构化绑定解包,读起来很自然。
注意:tuple 不适合返回生命周期依赖函数栈的对象(比如局部 std::string 的引用),但返回值本身是值语义,安全。
auto get_min_max_sum(const std::vector<int>& v) { return std::make_tuple(*min, *max, sum); }</int>- 调用:
auto [mn, mx, s] = get_min_max_sum(v);(C++17 结构化绑定) - 如果只用其中一两个值,别用
std::tie绑定std::ignore,不如直接用std::get(t)拿指定项
返回自定义结构体(推荐用于语义明确的组合)
比起 std::tuple<int int double></int>,命名字段更易读、可维护。尤其当多个函数返回相同组合(比如坐标、状态码+消息),定义一个结构体能避免类型混淆和硬编码索引。
性能上和 tuple 几乎一致(都是 POD 或 trivially copyable),但调试器显示更友好,IDE 也能补全字段名。
struct ParseResult { bool ok; int code; std::string msg; };- 函数签名:
ParseResult try_parse(const char* s);,调用方直接写r.ok、r.msg - 不要为了“省事”在结构体里塞指针或裸引用——返回后可能悬空;所有成员应是值类型或带所有权的智能指针
避免用 std::pair 承载超过两个值
std::pair 只适合严格两个值的场景(比如哈希表的 key,value)。强行嵌套 std::pair<int std::pair std::string>></int> 会让调用方代码变得难读、难维护,也失去编译期类型检查意义。
错误示范:return std::make_pair(x, std::make_pair(y, z)); —— 这种写法在 review 时基本会被打回去。
- 两个值:用
std::pair或结构体都行,看语义是否通用(如std::map::insert返回std::pair<iterator></iterator>) - 三个及以上:一律用
std::tuple或具名结构体,别绕弯 - 如果发现经常要从 pair 里取
.second.second,说明设计已经失控,该重构了
真正容易被忽略的是生命周期管理:所有通过引用“返回”的变量,必须确保调用方传入的实参在函数返回后仍然有效;而所有通过值返回(tuple/struct)的对象,要注意移动语义是否被正确触发——特别是含大缓冲区的类型,别让编译器意外拷贝。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











