C++11中如何以引用方式传递可变数量的对象?
在C++11中以引用方式传递可变数量的对象
刚好我之前也碰到过类似的困扰——C风格可变参数不仅语法丑陋,还类型不安全;而直接用std::initializer_list又会默认传值,导致对象被复制,完全达不到操作原对象的目的。不过C++11里有两种优雅的解决方案,能完美满足你的需求,我给你一步步演示:
方案1:用std::reference_wrapper包装引用
std::initializer_list本身只能存储值类型,但我们可以用<functional>里的std::reference_wrapper来包装对象引用,它是可拷贝的,刚好能放进initializer_list里。
首先补全你的MyClass定义(方便测试):
#include <iostream> #include <initializer_list> #include <functional> // 别忘了包含这个头文件 class MyClass { public: MyClass() : m_id(0), m_idChecks(0) {} ~MyClass(){} int getId() { m_idChecks++; return m_id; } int getIdChecks() const { return m_idChecks; } void setId(int id) { m_id = id; } private: int m_id; int m_idChecks; };
然后定义接收包装后引用的函数:
void processObjects(std::initializer_list<std::reference_wrapper<MyClass>> objects) { for (auto& obj_wrap : objects) { // 用get()获取原始的对象引用 MyClass& obj = obj_wrap.get(); std::cout << "当前对象ID:" << obj.getId() << std::endl; } }
测试一下效果:
int main() { MyClass obj1, obj2, obj3; obj1.setId(10); obj2.setId(20); obj3.setId(30); // 调用时直接传对象,语法和传值的initializer_list几乎一样 processObjects({obj1, obj2, obj3}); // 验证原对象的计数是否正确累加 std::cout << "obj1的getId被调用:" << obj1.getIdChecks() << "次\n"; std::cout << "obj2的getId被调用:" << obj2.getIdChecks() << "次\n"; std::cout << "obj3的getId被调用:" << obj3.getIdChecks() << "次\n"; return 0; }
运行后你会看到,三个对象的getIdChecks都只增加了1次,说明我们操作的都是原对象,没有发生复制。
方案2:使用可变模板参数(Variadic Templates)
如果不需要把这些对象放进一个集合里处理,可变模板参数会是更简洁的选择,它直接传递每个对象的引用,不需要额外包装:
// 定义一个模板函数,接收任意数量的MyClass引用 template<typename... Args> void processObjects(Args&... objects) { // 用C++11的方式展开参数包(借助initializer_list) (void)std::initializer_list<int>{ (std::cout << "当前对象ID:" << objects.getId() << std::endl, 0)... }; }
调用的时候更简单,直接传对象就行,不用加大括号:
int main() { MyClass obj1, obj2, obj3; obj1.setId(10); obj2.setId(20); obj3.setId(30); processObjects(obj1, obj2, obj3); std::cout << "obj1的getId被调用:" << obj1.getIdChecks() << "次\n"; std::cout << "obj2的getId被调用:" << obj2.getIdChecks() << "次\n"; std::cout << "obj3的getId被调用:" << obj3.getIdChecks() << "次\n"; return 0; }
这个方案的语法更清爽,而且完全没有额外的开销,非常适合简单的批量处理场景。
两种方案的对比
std::reference_wrapper+initializer_list:适合需要将对象集合保存起来(比如放进容器)的场景,调用语法和传统initializer_list一致,容易理解。- 可变模板参数:调用更简洁,性能开销更小,适合直接对每个对象做相同操作的场景。
这两种方法都完美避开了C风格可变参数的缺点,也解决了initializer_list传值的问题,完全符合C++11的优雅风格~
内容的提问来源于stack exchange,提问作者Jimmy Pettersson
相关产品推荐
相关产品推荐

