如何实现可检测派生类受保护构造的std::is_constructible等效方案?
等效于std::is_constructible的受保护构造检测方案
没问题,我来给你捋清楚怎么实现这个需求——要检测假设的派生类能否通过被测类的受保护构造函数完成构造,std::is_constructible确实派不上用场,因为它是从外部非友元、非派生的上下文判断构造可行性的,受保护成员对它来说属于不可访问范畴,会直接返回false,但实际上派生类是有权访问基类受保护构造的。
核心思路
我们可以创建一个临时的测试派生类,在这个派生类的上下文里尝试调用被测类的构造函数,利用SFINAE(替换失败不是错误)的特性来判断构造是否可行。因为测试类是被测类的派生类,所以天然拥有受保护成员的访问权限,这样就能准确模拟真实派生类的构造场景。
具体实现代码
#include <type_traits> #include <utility> namespace detail { // 定义一个继承自Base的测试类,用于模拟派生类上下文 template <typename Base, typename... Args> struct test_derived : Base { // 如果Base(Args...)构造可行,这个重载会被选中,返回std::true_type static auto test(int) -> decltype(Base(std::declval<Args>()...), std::true_type{}); // 构造不可行时,会 fallback 到这个重载,返回std::false_type static auto test(...) -> std::false_type; }; } // 主模板,继承自测试类的test函数返回值类型 template <typename Base, typename... Args> struct is_constructible_from_protected : decltype(detail::test_derived<Base, Args...>::test(0)) {}; // 可选:添加变量模板,方便直接使用值 template <typename Base, typename... Args> constexpr bool is_constructible_from_protected_v = is_constructible_from_protected<Base, Args...>::value;
代码工作原理
detail::test_derived是被测类Base的派生类,所以它的成员函数test可以合法访问Base的受保护构造函数。- 当我们调用
test(0)时,编译器会优先尝试匹配第一个test重载:如果Base(std::declval<Args>()...)这个构造表达式合法,就会返回std::true_type;如果构造不合法(比如参数类型不匹配、构造函数不存在),SFINAE会让这个重载失效,转而匹配第二个返回std::false_type的重载。 - 最终
is_constructible_from_protected的类型由decltype推导的返回值决定,完美模拟了派生类视角下的构造可行性。
使用示例
// 测试用的基类,包含受保护构造函数 class MyBase { protected: MyBase(int) {} MyBase(double, bool) {} public: MyBase() = default; }; // 验证检测结果 static_assert(is_constructible_from_protected_v<MyBase>, "无参构造(公共)应该可行"); static_assert(is_constructible_from_protected_v<MyBase, int>, "int参数的受保护构造应该可行"); static_assert(is_constructible_from_protected_v<MyBase, double, bool>, "double+bool的受保护构造应该可行"); static_assert(!is_constructible_from_protected_v<MyBase, float>, "float参数的构造不存在,应该不可行");
扩展:检测特定派生类的构造可行性
如果你的需求是检测某个具体派生类能否调用基类的受保护构造,也可以调整实现,把派生类作为模板参数传入:
namespace detail { template <typename Derived, typename Base, typename... Args> auto test_specific_derived(int) -> decltype( // 在Derived的上下文里尝试构造Base(模拟Derived的初始化列表调用) static_cast<Derived*>(nullptr)->Base(std::declval<Args>()...), std::true_type{} ); template <typename Derived, typename Base, typename... Args> auto test_specific_derived(...) -> std::false_type; } template <typename Derived, typename Base, typename... Args> struct is_constructible_from_protected_by : std::enable_if_t<std::is_base_of_v<Base, Derived>, decltype(detail::test_specific_derived<Derived, Base, Args...>(0))> {}; template <typename Derived, typename Base, typename... Args> constexpr bool is_constructible_from_protected_by_v = is_constructible_from_protected_by<Derived, Base, Args...>::value;
这个版本会先通过std::is_base_of确保Derived确实是Base的派生类,再检测构造可行性。
内容的提问来源于stack exchange,提问作者S. Paris
相关产品推荐
相关产品推荐

