向接收一元谓词参数的函数传std::isspace时GCC编译报错如何解决
问题根因
std::isspace、std::isgraph 这类C标准库字符判断函数存在多个重载版本:除了从C标准库继承的int (int)签名版本,<locale>头文件中还定义了携带locale参数的重载版本。
MSVC做了非标准的编译器扩展,在std::function构造场景下会自动匹配签名符合的重载版本,因此可以直接编译通过;但GCC严格遵循C标准,重载函数本身不具备确定的函数类型,在需要做类型转换/类型推导的场景下,编译器无法自动选定你需要的重载版本,因此抛出重载解析失败的错误。
可行修改方案
方案1:保留原有trim函数定义,修正调用逻辑
不需要改动trim函数的参数类型,只需要在传入标准库重载函数时,显式指定匹配的函数签名,消除歧义即可:
// 显式转换为int(int)签名的函数指针,明确指定要使用的重载 const auto trimmed = trim(test, static_cast<int(*)(int)>(std::isspace)); const auto trimmed2 = trim(test, std::not_fn(static_cast<int(*)(int)>(std::isgraph))); // 自定义lambda无重载歧义,不需要修改 const auto trimmed3 = trim(test, [](int ch) { return ch == ' '; });
额外提示:直接传入C标准库的字符判断函数存在未定义行为风险:这类函数要求入参必须是EOF或者可转换为
unsigned char类型的值,如果传入的char为负值(比如UTF-8、GBK编码中高位为1的多字节字符)会触发异常。更稳妥的写法是用lambda做一层安全包装:const auto trimmed = trim(test, [](unsigned char ch) { return std::isspace(ch); });
方案2:修改trim函数参数为泛型可调用类型(推荐)
将第二个参数从固定的std::function<bool(int)>改为模板泛型参数,既可以避免std::function带来的类型擦除运行时开销,也能兼容更多可调用对象类型:
template<typename TrimPred> std::string trim(const std::string& str, TrimPred isCharToTrim) { auto trimmedStart = std::find_if_not(str.begin(), str.end(), isCharToTrim); if (trimmedStart == str.end()) { return ""; } auto trimmedEnd = std::find_if_not(str.rbegin(), str.rend(), isCharToTrim); return std::string(trimmedStart, trimmedEnd.base()); }
使用该版本时,传入标准库重载函数依然需要做上述的显式签名转换,其余自定义lambda、函数对象、函数指针均可直接传入,无需额外包装。
内容的提问来源于stack exchange,提问作者Synck
相关产品推荐
相关产品推荐

