为何存在更具体构造函数时仍调用万能引用构造函数?
在代码中执行entity a{n};时,本应调用const entity&版本的拷贝构造函数,却意外触发了带万能引用的模板构造函数——即便添加了requires (!std::same_as<std::remove_cvref<T>, entity>)约束,问题依然存在,还会引发后续无限递归风险。
问题成因
头文件缺失导致约束失效
代码使用了std::remove_cvref和std::same_as,但未包含<type_traits>头文件。这两个类型特性模板由<type_traits>提供,缺失头文件会让编译器无法正确解析约束条件,原本用来排除entity类型的requires语句形同虚设。重载匹配优先级问题
即便约束生效,当传入非const的entity左值(如示例中的n)时,模板构造函数可实例化为entity(entity&),该版本参数与实参完全匹配;而显式声明的拷贝构造函数是entity(const entity&),需要将非const左值转换为const引用,匹配度低于前者,因此编译器会优先选择模板构造函数。
解决方法
步骤1:补充必要头文件
在代码开头添加<type_traits>,确保类型特性模板正常工作:
#include <type_traits>
步骤2:优化约束条件
将requires约束调整为更严谨的形式,完全排除所有entity相关类型(包括cv限定、引用等):
template <typename T> requires (!std::is_same_v<std::remove_cvref_t<T>, entity>) entity(T&& other) { printf("entity(T&&)\n"); // 补充functor的初始化逻辑 }
也可以用std::decay_t替代std::remove_cvref_t,二者都能剥离引用和cv限定后判断类型是否为entity。
步骤3:贴合设计意图的约束(可选)
如果模板构造函数的核心目的是接收functor类型,可以直接约束模板仅接受能转换为functor_t*的类型,从根源避免处理entity类型:
template <typename T> requires (std::convertible_to<T, functor_t*>) entity(T&& other) { printf("entity(T&&)\n"); functor_ = static_cast<functor_t*>(std::forward<T>(other)); // 补充对应的资源初始化逻辑 }
修改后验证
修改后执行entity a{n};会正确调用entity(const entity&),b = a;也会触发正确的拷贝逻辑,输出应为:
entity(const entity&) entity(const entity&) ~entity() -------------------- ~entity() ~entity() ~entity()
内容的提问来源于stack exchange,提问作者glades

