如何使用SFINAE检测CTAD类模板推导指引的explicit属性
关于explicit类模板推导指引的编译期检测实现
结论
C++17及以上标准均可实现你需要的has_properly_explicit_deduction_guide_v检测能力,不需要依赖未来的标准特性。
核心原理
利用你观察到的explicit推导指引的行为差异:
- 非explicit推导指引支持拷贝初始化形式的CTAD:
T var = 参数可以正常完成推导 - explicit推导指引仅支持直接初始化的CTAD,不支持拷贝初始化形式的CTAD
通过SFINAE或者C++20 concept检测这两种初始化的合法性,即可区分两种推导指引。
C++20 实现版本
#include <utility> // 检测给定参数类型下,类模板T是否支持拷贝初始化形式的CTAD template<template<typename...> class T, typename Arg> concept has_implicit_ctad = requires(Arg&& arg) { { T = std::forward<Arg>(arg) }; }; // 检测给定参数类型下,类模板T是否只有explicit的推导指引 template<template<typename...> class T, typename Arg> concept has_explicit_ctad = requires(Arg&& arg) { // 首先确保直接初始化的CTAD是合法的 T(std::forward<Arg>(arg)); // 同时拷贝初始化的CTAD不合法 requires !has_implicit_ctad<T, Arg>; }; // 测试用的A、B类 template<class T> struct A { A(int); }; A(int) -> A<int>; template<class T> struct B { B(int); }; explicit B(int) -> B<int>; // 验证结果符合预期 static_assert(!has_explicit_ctad<A, int>); static_assert(has_explicit_ctad<B, int>);
C++17 实现版本
C++17没有concept语法,改用SFINAE实现相同逻辑:
#include <type_traits> #include <utility> template<template<typename...> class T, typename Arg, typename = void> struct has_implicit_ctad : std::false_type {}; template<template<typename...> class T, typename Arg> struct has_implicit_ctad<T, Arg, std::void_t<decltype(T = std::declval<Arg>())>> : std::true_type {}; template<template<typename...> class T, typename Arg, typename = void> struct has_explicit_ctad : std::false_type {}; template<template<typename...> class T, typename Arg> struct has_explicit_ctad<T, Arg, std::enable_if_t< std::is_constructible_v<decltype(T(std::declval<Arg>())), Arg> && !has_implicit_ctad<T, Arg>::value >> : std::true_type {}; // 对外提供的变量模板 template<template<typename...> class T, typename Arg> constexpr bool has_properly_explicit_deduction_guide_v = has_explicit_ctad<T, Arg>::value; // 测试用的A、B类 template<class T> struct A { A(int); }; A(int) -> A<int>; template<class T> struct B { B(int); }; explicit B(int) -> B<int>; // 验证结果符合预期 static_assert(!has_properly_explicit_deduction_guide_v<A, int>); static_assert(has_properly_explicit_deduction_guide_v<B, int>);
注意事项
- 检测逻辑和传入的参数类型绑定,你需要根据类模板实际支持的推导参数类型传入对应的类型参数,比如如果类模板支持
std::string类型的推导,就将第二个模板参数换成std::string即可。 - 目前C++标准还没有提供直接反射类模板所有推导指引的语法,因此上述实现都是基于可观测行为的检测,完全可以满足单元测试的需求。
内容的提问来源于stack exchange,提问作者Quuxplusone
相关产品推荐
相关产品推荐

