最推荐用 std::tuple 或自定义 struct 返回多个值:tuple 适合类型各异、数量固定的场景,struct 更安全可读;避免输出参数引发的生命周期和竞态问题。

用 std::tuple 返回多个不同类型的值
直接 return 多个值在 C++ 里语法不合法,但 std::tuple 是最通用的解法,尤其适合类型各异、数量固定(比如 2–4 个)的返回场景。它不修改原有函数签名逻辑,调用侧也能明确解包。
常见错误是忘记用 std::get<i>(t)</i> 或结构化绑定时漏写 auto 或类型不匹配:
- 没开启 C++17 就用结构化绑定会编译失败 —— 确认编译器支持并加
-std=c++17 -
std::get(result)索引越界不会编译报错,但运行时 UB;务必核对 tuple 元素数量和索引 - 返回局部
tuple没问题,但别返回内部含裸指针或临时对象引用的 tuple
示例:
std::tuple<int std::string double> get_user_data() {
return {42, "Alice", 3.14};
}
// 调用
auto [id, name, score] = get_user_data(); // C++17 结构化绑定
// 或
auto result = get_user_data();
int id = std::get(result);
</int>
用结构体(struct)返回多个命名值
当返回值有清晰语义、可能复用或需文档化时,自定义 struct 比 tuple 更安全、可读性更高。成员名即文档,IDE 支持补全,也避免索引错位问题。
容易踩的坑集中在内存和生命周期上:
- 结构体含非 trivial 类型(如
std::vector)时,移动语义默认启用,一般无需干预;但若手动写了拷贝构造,要检查是否意外深拷贝大对象 - 别在函数里返回局部结构体的指针或引用 —— 对象生命周期结束,悬垂指针立刻出问题
- 如果结构体只用于单次返回,且成员都是 POD 类型,可以考虑加
[[nodiscard]]防止调用者忽略返回值
示例:
struct UserInfo {
int id;
std::string name;
double score;
};
UserInfo get_user_data() {
return {42, "Alice", 3.14};
}
// 调用
UserInfo u = get_user_data();
std::cout
<h3>用输出参数(<code>&</code> 或 <code>std::reference_wrapper</code>)就地写入</h3>
<p>适用于需要复用已有变量、避免拷贝开销,或调用方必须控制内存位置的情况(例如嵌入式、实时系统)。本质是“传入变量,函数填值”,不是真正“返回”。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>风险点很集中:引用有效性完全由调用方保证,函数本身无法校验:</p>
- 传入临时变量的引用(如
func(x, std::string{"tmp"}))会导致绑定到已销毁对象 - 多线程环境下,多个输出参数若指向同一对象,可能引发竞态 —— 必须加锁或确保隔离
- 使用
std::ref(x)传参时,别误写成std::ref(&x),后者是引用的引用,编译不过
示例:
void get_user_data(int& id, std::string& name, double& score) {
id = 42;
name = "Alice";
score = 3.14;
}
// 调用
int id; std::string name; double score;
get_user_data(id, name, score);
为什么不用 std::pair 或数组?
std::pair 只适合两个值,且 .first/.second 语义弱,三人及以上就硬凑很别扭;std::array 要求同类型,灵活性差,还容易和 C 风格数组混淆。
更关键的是可维护性问题:
- 用
std::array<int></int>返回坐标{x,y,z},后续想加单位字段就得改所有调用点,而结构体只需加成员 -
std::pair<:string int></:string>和std::pair<int std::string></int>类型等价但语义相反,传错参数编译器不报错,运行时才发现 - 所有基于模板容器的方案(包括
tuple)在调试器里展开较深,不如结构体字段一目了然
除非明确受限于接口契约(比如 STL 算法要求返回 pair),否则优先选 struct 或 tuple。
实际项目里,结构体和 tuple 占比最高;输出参数仅在性能敏感路径或 legacy 接口适配时出现。别为了“看起来像返回多个值”而牺牲可读性或引入悬垂引用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










