为何此处需要将std::string显式转换为std::string_view?
先看你编写的函数模板:
#include <string> #include <string_view> template <typename CharT, typename Traits = std::char_traits<CharT>> int fun(std::basic_string_view<CharT, Traits> str1) { return 0; }
当用std::string直接调用时:
int main() { std::string s1("String 1"); fun(s1); // 编译错误 }
编译报错如下:
/usr/bin/g++ -fdiagnostics-color=always -g /home/jeff/Projects/xxx/jkb.cpp -o /home/jeff/Projects/xxx/jkb -std=c++17 -I../include /home/jeff/Projects/xxx/jkb.cpp: In function ‘int main()’: /home/jeff/Projects/xxx/jkb.cpp:20:8: error: no matching function for call to ‘fun(std::string&)’ 20 | fun(s1); | ~~~^~~~ /home/jeff/Projects/xxx/jkb.cpp:6:5: note: candidate: ‘template<class CharT, class Traits> int fun(std::basic_string_view<_CharT, _Traits>)’ 6 | int fun(std::basic_string_view<CharT, Traits> str1) | ^~~ /home/jeff/Projects/xxx/jkb.cpp:6:5: note: template argument deduction/substitution failed: /home/jeff/Projects/xxx/jkb.cpp:20:8: note: ‘std::__cxx11::basic_string<char>’ is not derived from ‘std::basic_string_view<_CharT, _Traits>’ 20 | fun(s1); | ~~~^~~~
显式转换为std::string_view后就能正常运行:
fun(static_cast<std::string_view>(s1));
为什么隐式转换会失败?
核心原因是模板参数推导的规则限制:
在模板函数的参数推导阶段,编译器只会尝试直接匹配实参和形参的类型,不会自动触发隐式类型转换(仅有的例外是数组转指针、函数转函数指针这类内置转换)。
当你传入std::string时,编译器需要推导CharT和Traits这两个模板参数,但std::string(即std::basic_string<char>)和std::basic_string_view<CharT, Traits>是完全不同的类型,编译器无法从std::string推导出CharT和Traits应该是什么,推导直接失败,根本没机会触发std::string_view接受std::string的隐式构造函数。
而显式转换时,你已经把std::string转成了std::string_view,此时编译器可以直接从这个实参推导出CharT=char、Traits=std::char_traits<char>,模板匹配成功。
为什么报错说string不是从string_view派生?
这是编译器推导失败后给出的一种“猜测性提示”。编译器发现实参和形参类型不匹配,它会尝试检查是否存在派生关系(因为派生类可以隐式转换为基类,这是模板推导中少数允许的转换场景之一),但std::string和std::string_view本身没有任何继承关系,所以编译器就给出了这个提示。
这个提示有点误导性,但本质还是想说明:实参类型和形参的模板类型之间没有能让推导成功的转换关系。
解决方法
除了显式转换之外,还有两种更优雅的方式:
- 提供非模板重载:
int fun(std::string_view str1) { return 0; }
非模板函数的优先级高于模板函数,std::string可以隐式转换为std::string_view,调用时会直接匹配这个重载。
- 用模板约束允许可转换类型:
template <typename T> requires std::convertible_to<T, std::basic_string_view<typename T::value_type>> int fun(T&& str) { std::basic_string_view<typename T::value_type> sv(std::forward<T>(str)); return 0; }
通过requires约束确保实参可以转换成对应的string_view,同时避免推导失败的问题。
内容的提问来源于stack exchange,提问作者jkb

