如何避免三元运算符中的不必要拷贝?含大型对象通用场景问询
问题分析与解决方案
首先看你给出的代码:三元运算符的两个分支,一个是const std::string&类型的s,另一个是临时std::string对象('{' + s + '}'的结果)。C++中三元表达式的结果类型是两个分支的公共类型,这里会被推导为std::string(值类型),所以即使条件为真,也会把s拷贝到临时对象再绑定到s2,这就产生了不必要的字符串拷贝。
针对这个问题,以及你提到的大型对象避免不必要拷贝的通用场景,以下是几种可行的技巧:
1. 分支分离(简单直接)
直接用if-else拆分逻辑,不需要修改时直接引用原对象,需要修改时才创建新对象:
bool f(const std::vector<std::string>& vec, const std::string& s) { if (!s.empty() && s[0] == '{') { // 直接用原对象,无任何拷贝 for (const std::string& v : vec) { if (v == s) return true; } } else { // 仅此处生成修改后的对象,产生一次拷贝 std::string modified = '{' + s + '}'; for (const std::string& v : vec) { if (v == modified) return true; } } return false; }
这种方式没有额外的模板或类开销,代码逻辑清晰,适合简单场景。
2. 使用std::variant统一处理(通用场景)
用std::variant存储原对象的引用或修改后的对象,通过std::visit统一访问,适合需要对两种状态做相同逻辑处理的场景:
#include <variant> #include <functional> // 用于std::cref bool f(const std::vector<std::string>& vec, const std::string& s) { std::variant<const std::string&, std::string> s_storage; if (!s.empty() && s[0] == '{') { s_storage = std::cref(s); // 存储原对象的引用,无拷贝 } else { s_storage = '{' + s + '}'; // 存储修改后的临时对象,仅此时拷贝 } // 统一获取const引用 const std::string& s2 = std::visit([](const auto& val) -> const std::string& { return val; }, s_storage); for (const std::string& v : vec) { if (v == s2) return true; } return false; }
3. 通用包装类(复用性强)
如果需要在多个地方复用这种逻辑,可以写一个通用的包装类,内部维护原对象引用或修改后的对象:
#include <variant> #include <functional> #include <string> #include <vector> template<typename T> class MaybeModified { public: // 构造函数:传入原对象、是否需要修改、修改函数 template<typename ModifyFunc> MaybeModified(const T& original, bool need_modify, ModifyFunc modify_func) { if (!need_modify) { storage_.emplace<0>(std::cref(original)); } else { storage_.emplace<1>(modify_func(original)); } } // 获取统一的const引用 const T& get() const { return std::visit([](const auto& val) -> const T& { return val; }, storage_); } private: std::variant<std::reference_wrapper<const T>, T> storage_; }; // 使用示例 bool f(const std::vector<std::string>& vec, const std::string& s) { bool need_modify = !(s.empty() && s[0] == '{'); MaybeModified<std::string> s_mod(s, need_modify, [](const std::string& str) { return '{' + str + '}'; }); const std::string& s2 = s_mod.get(); for (const std::string& v : vec) { if (v == s2) return true; } return false; }
核心思路总结:只在必须修改对象的时候才创建新对象,否则直接引用原对象,避免无意义的拷贝操作。
内容的提问来源于stack exchange,提问作者SU3
相关产品推荐
相关产品推荐

