最直接标准的做法是用 std::tuple,c++17 起配合结构化绑定更安全可读;具名 struct 更清晰可维护;std::pair 适合二元组合;应避免输出参数破坏纯度和扩展性。

用 std::tuple 打包多类型返回值
最直接、标准的做法是用 std::tuple,它能容纳任意数量、任意类型的值,且零开销。C++17 起配合结构化绑定,写法非常干净。
常见错误是手动解包时顺序或类型写错,比如把 std::get(t) 当成 int 却实际是 std::string,编译期报错但信息不够直观。
- 必须包含头文件:
#include <tuple></tuple> - C++17 推荐用结构化绑定:
auto [x, s, flag] = my_func();,比std::get<n></n>更安全、可读 - 注意
std::tuple是值语义,大对象(如std::vector)会被拷贝;需要避免拷贝时考虑std::tuple<const std::vector>&></const>或返回std::pair<t u></t>等更轻量组合
std::tuple<int std::string bool> get_user_info() {
return {42, "Alice", true};
}
// C++17 使用
auto [id, name, active] = get_user_info(); // 直接解包</int>
用 struct 显式命名返回字段
当返回值有明确业务含义(比如“用户ID、用户名、是否已激活”),用具名 struct 比 tuple 更清晰、可维护。它还能加成员函数、默认构造、operator== 等。
容易踩的坑是定义成裸 struct 却忘了加 constexpr 或移动语义支持,在性能敏感路径下可能触发多余拷贝。
- 推荐定义为
struct而非class(默认 public,语义更贴合数据载体) - 若字段较多或需序列化,考虑加
[[nodiscard]]防止调用者忽略返回值 - 避免在
struct里放非 trivial 类型(如含虚函数、自定义析构)除非真有必要,否则影响返回效率
struct UserInfo {
int id;
std::string name;
bool active;
};
UserInfo get_user_info() {
return {42, "Alice", true}; // 聚合初始化,C++11 起支持
}
用 std::pair 处理恰好两个值的场景
std::pair 是 tuple 的特例,专为两个值设计,语法更短、语义更聚焦。适合“键-值”“成功-结果”“数值-单位”这类固定二元组合。
常见误用是强行塞三个东西进去,比如用 std::pair<int std::pair bool>></int>,嵌套可读性差,不如直接上 tuple 或 struct。
- 字段名不直观:只能用
.first/.second,建议只用于临时、内部逻辑,对外接口优先用具名struct - 和
tuple一样支持结构化绑定:auto [code, msg] = http_request(); - 移动语义天然支持,小对象返回无额外开销
std::pair<int std::string> parse_int(const std::string& s) {
try {
return {std::stoi(s), ""};
} catch (...) {
return {-1, "invalid format"};
}
}</int>
别用输出参数(int&、std::string&)替代返回值
虽然技术上可行,但会破坏函数纯度、增加调用方负担、难以链式调用,且容易引发生命周期问题(比如传入临时对象的引用)。
典型错误是写成 void f(int& a, std::string& b, bool& c),调用时必须先声明变量再传入,无法用于 auto 初始化或 lambda 内联表达式。
- 输出参数无法参与 NRVO(命名返回值优化),而
tuple/struct返回值可以 - 如果函数本就该返回多个值,就该让它“返回”,而不是“修改外部状态”
- 唯一合理使用场景:确实需要复用已有变量(如缓冲区重用),且性能压倒一切 —— 但这是例外,不是默认选择
真正麻烦的地方不在语法,而在语义:一旦函数签名带多个输出参数,后续扩展(比如加第四个返回值)就得改调用点所有地方;而 tuple 或 struct 只需改一处定义,调用侧用结构化绑定自动适配。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











