向接收std::string的函数传参时,std::string_view有何优势?
关于std::string_view作为unordered_map查找参数的疑问解答
首先,咱们得先搞清楚为什么你的代码编译不过:std::unordered_map<std::string, OpEntry>的find方法只接受能隐式转换为std::string的参数,而std::string_view和std::string之间没有隐式转换(毕竟string_view是视图,不拥有内存,而string是容器,拥有内存),所以你必须显式构造std::string(op)才能让find正常工作。
接下来回到你的核心问题:这种情况下用std::string_view做参数,还有优势吗?是不是还不如直接用const std::string&?答案得看你的调用场景和能不能修改opTable的类型,咱们分情况说:
情况1:你无法修改opTable的类型(必须用unordered_map<std::string, OpEntry>)
这时候得看调用getOpCount的参数来源:
- 如果调用方大多传的是已经存在的
std::string对象:
用const std::string&更优。因为传const string&时,参数直接绑定到原string,find可以直接用这个引用,不需要任何额外的内存分配或拷贝;但如果用string_view,你在函数里必须构造一个新的std::string,相当于多做了一次拷贝(如果是长字符串,还会有堆分配),反而比const string&慢。 - 如果调用方经常传字符串字面量、
char*、std::string_view或者字符串子串:
这时候string_view的灵活性就体现出来了。比如调用方传"add"字面量时,用const string&会先构造一个临时std::string(因为字面量要转成const string&必须生成临时对象),而用string_view的话,你在函数里构造std::string,两者的开销其实差不多(都是一次string构造),但string_view可以直接接受所有这些类型的输入,不需要调用方先手动转成std::string。
另外,如果是短字符串(比如你的操作符名字),std::string的**小字符串优化(SSO)**会生效,构造时不需要堆分配,这时候两种方式的性能差异几乎可以忽略。
情况2:你可以修改opTable的类型
这才是发挥std::string_view优势的最佳场景,但有个前提:opTable里所有key的生命周期必须比opTable长(比如key是字符串字面量、全局静态std::string,或者其他不会提前销毁的字符串)。
这时候把opTable改成std::unordered_map<std::string_view, OpEntry>,你的代码就能直接用string_view查找,完全不需要构造std::string:
// 假设所有key都是生命周期足够长的字符串(比如字面量) const static std::unordered_map<std::string_view, OpEntry> opTable = { {"add", OpEntry{/* 初始化数据 */}}, {"sub", OpEntry{/* 初始化数据 */}}, // ... 其他操作符 }; int Scanner::getOpCount(std::string_view op) { auto itr = Parser::opTable.find(op); if (itr != Parser::opTable.end()) { return itr->second.count; // 假设OpEntry有count成员 } return 0; // 处理未找到的情况 }
这种情况下,string_view的优势拉满:既避免了不必要的字符串拷贝,又能接受所有类型的字符串输入(字面量、char*、string、string_view等),效率和灵活性都兼顾了。
总结
- 不能改
opTable时:如果调用方以std::string为主,选const std::string&;如果需要兼容多种字符串输入,选std::string_view(短字符串场景开销可忽略)。 - 能改
opTable且key生命周期足够长时:果断换成unordered_map<std::string_view, OpEntry>,配合string_view参数,这是最优解。
内容的提问来源于stack exchange,提问作者jeanluc
相关产品推荐
相关产品推荐

