使用std::empty作为谓词时模板类型推导失败的解决方法
问题分析与快速解决
嘿,这个问题本质是函数模板的重载歧义导致的编译失败,我给你拆解原因,再给几个最简单的解决办法:
最推荐的快速解法:用Lambda表达式
直接用lambda封装std::empty的调用,这是兼容性最好、可读性最高的写法,所有主流编译器(GCC、Clang、MSVC)都能完美编译:
#include <algorithm> #include <vector> int main() { std::vector<std::vector<int>> vv; bool all_subvectors_empty = std::all_of( std::begin(vv), std::end(vv), [](const auto& sub_vec) { return std::empty(sub_vec); } ); }
lambda会明确告诉编译器:我们要对每个子vector调用std::empty,完全消除了重载歧义的问题。
另一种解法:显式指定并强制转换std::empty类型
如果你不想用lambda,也可以通过显式指定std::empty的模板参数,再强制转换为对应函数指针类型来消除歧义,但这种写法相对繁琐:
#include <algorithm> #include <vector> int main() { std::vector<std::vector<int>> vv; // 先定义我们需要的函数指针类型 using EmptyFuncPtr = bool(*)(const std::vector<int>&); bool all_subvectors_empty = std::all_of( std::begin(vv), std::end(vv), static_cast<EmptyFuncPtr>(std::empty<std::vector<int>>) ); }
为什么原代码会编译失败?
你观察到的编译器差异(GCC 6.3能过,其他版本/编译器失败),核心原因是:
std::empty是一组重载的函数模板:它有针对任意容器的通用版本,还有专门针对std::initializer_list的版本,当你直接把std::empty传给std::all_of时,编译器无法从这组重载里自动选出合适的实例。- 早期GCC版本可能在模板推导上做了宽松处理,但后续编译器严格遵循标准后,这种模糊的重载推导就会被拒绝,甚至触发内部编译器错误(比如GCC trunk版本)。
你自己写的极简复现代码也验证了这一点:当存在多个同模板参数的重载函数时,哪怕显式指定了模板参数,编译器还是需要你明确指出要用哪个函数指针类型,否则会陷入歧义。
内容的提问来源于stack exchange,提问作者Shmuel Hanoch
相关产品推荐
相关产品推荐

