C++ 多返回值的几种实现方式
在 C++ 中,函数默认只能返回一个值,不像 Python 那样可以 return a, b
。
不过我们仍然有很多方法可以实现“多返回值”。本文将从最简单的引用方式,到 tuple
、pair
和 struct
,逐一介绍并对比优缺点。
1. 引用参数(最常见,效率高)
static void ParseShader(const std::string& filepath, std::string& vertexSource, std::string& fragmentSource)
{vertexSource = "...";fragmentSource = "...";
}int main()
{std::string vs, fs;ParseShader("shader_path", vs, fs);// vs 和 fs 已被函数修改
}
优点
高效:避免拷贝大对象。
简洁:语法像普通变量,不用写
*
或->
。安全:不会为空指针。
灵活:
const T&
可读,T&
可写。
缺点
调用时不直观:看不出参数是否会被修改。
滥用易产生副作用(外部变量被改)。
有悬空引用风险(返回局部引用)。
绑定后不可更改对象,不如指针灵活。
2. 返回数组/指针(不推荐)
static std::string* ParseShader(const std::string& filepath)
{return new std::string[]{ "vs", "fs" }; // 堆分配
}
问题:
返回的是堆上指针,调用者必须手动
delete[]
数组大小信息丢失,不安全
容易内存泄漏
除非特殊情况,否则不建议这样写。
3. std::array / std::vector(适合同类型返回值)
/* std::array<std::string, 2>std::array 是 C++11 提供的一个定长数组容器。模板参数 <std::string, 2> 表示:里面存 2 个 std::string 类型的元素。所以 std::array<std::string, 2> 就相当于:struct {std::string elem[2];
};
*/static std::array<std::string, 2> ParseShader(const std::string& filepath)
{return { "vs", "fs" };
}int main()
{auto sources = ParseShader("shader_path");std::cout << sources[0] << sources[1] << std::endl;
}
优点:安全,自动管理内存
缺点:只能存储相同类型,取值需要
[0]
、[1]
,可读性一般
4. std::tuple(支持不同类型)
/*std::tuple 是 C++11 提供的一个容器,可以把不同类型的数据打包在一起。和 std::array 不同的是:array 里所有元素必须是同一种类型(比如全是 std::string)。tuple 可以放不同类型,比如 (int, std::string, double)。*/static std::tuple<std::string, std::string> ParseShader(const std::string& filepath)
{return std::make_tuple("vs", "fs");
}int main()
{auto sources = ParseShader("shader_path");std::cout << std::get<0>(sources) << std::get<1>(sources);
}
优点:可以返回不同类型的值
缺点:必须用
std::get<0>
这种“魔法数字”访问,可读性差
5. std::pair(两值场景)
/*pair 是 C++ 标准库里最简单的「二元组」容器,
里面只能存放 两个元素,可以是同类型,也可以是不同类型。它内部就是:template <typename T1, typename T2>
struct pair {T1 first;T2 second;
};也就是说,pair 其实就是一个 带两个成员变量的结构体:first 和 second。*/static std::pair<std::string, std::string> ParseShader(const std::string& filepath)
{return { "vs", "fs" };
}int main()
{auto sources = ParseShader("shader_path");std::cout << sources.first << sources.second;
}
优点:比
tuple
简单,专门用于两个值缺点:
first
/second
可读性一般
6. struct / 自定义类型(最推荐 🚀)
struct ShaderProgramSource
{std::string VertexSource;std::string FragmentSource;
};static ShaderProgramSource ParseShader(const std::string& filepath)
{return { "vs", "fs" };/*这是结构体的聚合初始化(aggregate initialization)。
等价于:
ShaderProgramSource temp;
temp.VertexSource = "vs";
temp.FragmentSource = "fs";
return temp;*/}int main()
{auto sources = ParseShader("shader_path");std::cout << sources.VertexSource << sources.FragmentSource;
}
优点:最清晰,可读性最好
扩展性强:后续要加字段很方便
缺点:需要额外写一个结构体定义
总结对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
引用参数 | 高效,简单 | 调用时必须准备变量 | 工程中常用 |
数组/指针 | 灵活 | 容易内存泄漏,不安全 | 不推荐 |
std::array | 栈上存储,性能好 | 只能存放相同类型 | 返回定长、同类型数据 |
std::vector | 自动扩容,支持变长 | 堆上存储,开销略大 | 返回动态数据集合 |
tuple | 支持不同类型 | 访问可读性差 | 返回少量异构数据 |
pair | 简洁,专门用于两个值 | first/second 语义不清晰 | 返回两个值 |
struct | 最直观,字段语义清楚,可扩展 | 需要额外定义 | 工程开发首选 |
小型函数快速写:
pair
或tuple
工程开发、长期维护:struct 最佳