C++17替代C++20 requires实现条件构造函数的方法
C++17 等效实现方案
C17 没有原生的 concepts/requires 约束语法,我们可以通过 SFINAE(替换失败非错)规则 + 标准库类型萃取(type traits) 实现完全一致的重载选择逻辑。首先先修正原C20示例代码的两处问题:
- 第二个构造函数参数列表末尾多余的逗号,属于语法错误
data_t定义的条件分支错误:原代码无论has_type<T>是否成立最终都返回T类型,会导致两个构造函数参数列表完全一致引发重定义,按照需求逻辑,当T存在嵌套type别名时,data_t应该是typename T::type
第一步:实现has_type类型检测特性
C20中通过requires表达式实现的嵌套类型检测,在C17中可以通过std::void_t配合模板偏特化实现:
#include <type_traits> // 检测类型T是否存在嵌套别名type template <typename T, typename = void> struct has_type : std::false_type {}; template <typename T> struct has_type<T, std::void_t<typename T::type>> : std::true_type {}; template <typename T> constexpr bool has_type_v = has_type<T>::value;
第二步:实现someClass类逻辑
构造函数的条件约束通过std::enable_if_t实现:因为构造函数没有返回值,我们将SFINAE约束放在构造函数的默认模板参数位置,保证重载决议时只有满足条件的构造函数会被纳入候选集:
template <typename T> class someClass { public: // 修正后的data_t定义:有嵌套type时取T::type,否则取T本身 using data_t = std::conditional_t<has_type_v<T>, typename T::type, T>; // 约束1:T为算术类型时启用该构造 template <typename U = T, std::enable_if_t<std::is_arithmetic_v<U> && !has_type_v<U>, int> = 0> constexpr someClass(U const& _a, U const& _b) : a{_a}, b{_b} {} // 约束2:T存在嵌套type别名时启用该构造 template <typename U = T, std::enable_if_t<has_type_v<U>, int> = 0> constexpr someClass(data_t const& _a, data_t const& _b) : a{_a}, b{_b} {} private: const data_t a, b; };
逻辑一致性说明
- 当传入算术类型(
int/float/double等)作为模板参数T时,has_type_v<T>为false,第二个构造函数会因SFINAE被排除在重载候选集外,自动匹配第一个构造 - 当传入带嵌套
type别名的结构体/类作为T时,std::is_arithmetic_v<T>为false,第一个构造函数被排除,自动匹配第二个构造 - 如果传入的
T既不是算术类型,也没有嵌套type别名,两个构造函数都不会进入候选集,实例化会直接报错,和原C++20代码行为完全一致
使用示例
// 测试用自定义类型 struct x { using type = float; }; int main() { // 匹配算术版本构造 someClass<int> c1(1, 2); // 匹配带嵌套type版本构造,参数类型为float someClass<x> c2(1.5f, 2.5f); return 0; }
内容的提问来源于stack exchange,提问作者Chris G.
相关产品推荐
相关产品推荐

