在disable_if_same_or_derived中替换std::is_base_of_v的C++20方案探讨
C++20 解决万能引用与拷贝构造冲突的无完整类型依赖方案
核心思路:用C++20特性替代依赖完整类型的std::is_base_of_v
原方案的问题在于std::is_base_of_v需要类型完全定义才能判断继承关系,而C++20的概念(Concepts)和新类型萃取工具可以在类型不完整时安全工作,同时解决万能引用与拷贝构造的匹配歧义。
方案1:用std::same_as排除自身类型匹配
如果仅需要避免万能引用匹配到类自身(含cv/引用修饰的自身类型),直接用std::same_as结合std::remove_cvref_t做约束,不需要依赖类型完整:
#include <concepts> #include <type_traits> template <typename T> struct MyClass { // 拷贝构造函数 MyClass(const MyClass&) = default; // 万能引用构造:约束U不能是MyClass的任何cv/ref版本 template <typename U> requires (!std::same_as<std::remove_cvref_t<U>, MyClass>) MyClass(U&&) { // 转发逻辑实现 } };
std::same_as和std::remove_cvref_t仅做类型标识匹配,不涉及继承关系检查,完全支持不完整类型。
方案2:用std::derived_from处理派生类排除场景
如果需要同时排除派生类的匹配(对齐原方案需求),C++20的std::derived_from概念在类型不完整时会触发SFINAE而非编译错误,行为更友好:
#include <concepts> #include <type_traits> template <typename T> struct MyClass { MyClass(const MyClass&) = default; // 万能引用构造:排除U是MyClass或其派生类的情况 template <typename U> requires (!std::derived_from<std::remove_cvref_t<U>, MyClass>) MyClass(U&&) { // 转发逻辑实现 } };
若U是不完整的派生类,该约束会让模板自动被排除,不会像std::is_base_of_v那样直接报错。
方案3:兼容旧enable_if写法的C++20版本
如果习惯用enable_if风格,可结合C++20工具重构原别名模板:
#include <concepts> #include <type_traits> template <typename A, typename B> using disable_if_same_or_derived = std::enable_if_t< !std::same_as<std::remove_cvref_t<B>, A> && !std::derived_from<std::remove_cvref_t<B>, A> >; template <typename T> struct MyClass { MyClass(const MyClass&) = default; template <typename U, typename = disable_if_same_or_derived<MyClass, U>> MyClass(U&&) { // 转发逻辑实现 } };
关键细节
std::remove_cvref_t是C++20新增工具,可一次性移除类型的引用和cv修饰,比std::remove_reference_t+std::remove_cv_t的组合更简洁。- 概念写法相比传统SFINAE可读性更强,且在类型不完整场景下的错误处理更可控。
内容的提问来源于stack exchange,提问作者Juergen
相关产品推荐
相关产品推荐

