C++元编程类型存在性检测机制疑问:MongoDB代码解析
detect_clone_factory_type_member_impl的检测机制 先来看这段用于检测类型T是否包含clone_factory_type成员的代码:
template <typename T> struct detect_clone_factory_type_member_impl { struct Fallback { struct clone_factory_type {}; }; struct Derived : T, Fallback {}; using Yes = char[2]; using No = char[1]; template <typename U> static No& test(typename U::clone_factory_type*); template <typename U> static Yes& test(U*); static constexpr bool value = sizeof(test<Derived>(nullptr)) == sizeof(Yes); using type = typename std::integral_constant<bool, value>::type; };
你的困惑点在于:Derived同时继承T和Fallback,不管T有没有clone_factory_type,Derived看起来都应该有这个成员,为什么第一个test函数不会总被选中?核心原因是SFINAE规则和继承中的类型歧义,分两种情况拆解:
情况1:T没有clone_factory_type成员
此时Derived的clone_factory_type只能来自Fallback,typename Derived::clone_factory_type是明确合法的类型。调用test<Derived>(nullptr)时,第一个test模板的参数typename Derived::clone_factory_type*可以匹配(nullptr能隐式转换为该指针类型),因此选中第一个test,返回No&(对应char[1],大小为1),最终value为false,表示T没有目标成员。
情况2:T有clone_factory_type成员
这时Derived同时从T和Fallback继承了同名的clone_factory_type类型,编译器无法确定typename Derived::clone_factory_type指的是哪一个——这属于类型歧义。根据SFINAE规则(替换失败不是错误),第一个test模板会因为参数类型无法解析而被编译器排除,不会参与重载决议。
此时第二个test模板会被选中:它的参数是Derived*,nullptr可以隐式转换为该指针类型,返回Yes&(对应char[2],大小为2),最终value为true,表示T存在目标成员。
简单来说,这个技巧利用了"当继承导致类型歧义时,编译器无法解析该类型,从而触发SFINAE排除对应模板"的特性,实现了对目标成员存在性的检测。
内容的提问来源于stack exchange,提问作者Brian

