为何consteval函数内的模板可接受函数参数作为模板参数?
问题核心:consteval函数参数作为模板参数的两种情况差异
可正常编译的代码
#include <concepts> enum class unit_type { }; template <typename Candidate> concept unit_c = requires() { requires std::semiregular<Candidate>; { Candidate::type } -> std::same_as<const unit_type&>; }; struct unit { static constexpr unit_type type{}; }; template<unit_c auto unit_1, unit_c auto unit_2> struct unit_product { static constexpr unit_type type{}; }; template <unit_c Unit1, unit_c Unit2> consteval unit_c auto operator*(Unit1 unit_1, Unit2 unit_2) noexcept { return unit_product<unit_1, unit_2>{}; } int main() { constexpr unit_c auto a = unit{} * unit{}; return 0; }
编译报错的代码
template <int val> struct bla { }; consteval void bla_test(int val) { bla<val>; }
两者的核心差异在于参数是否属于编译期常量表达式,以及非类型模板参数的适配规则:
第一个案例:参数是满足
unit_c概念的编译期常量对象operator*的参数Unit1/Unit2是满足unit_c概念的类型,unit_c隐含了该类型支持常规构造复制,且调用时传入的unit{}是编译期常量表达式对象。- 模板
unit_product的参数是unit_c auto类型的非类型模板参数,要求传入的对象必须是「类型符合unit_c的编译期常量」——这里的unit_1/unit_2本身就是编译期可知的常量对象,完全符合非类型模板参数的要求,因此可以直接传递。
第二个案例:参数是普通int,不属于编译期常量表达式
bla_test的参数val是普通int,哪怕bla_test是consteval函数(要求函数体在编译期执行),这个参数本身并不是常量表达式——它的取值是在函数调用时确定的(哪怕调用发生在编译期),但不具备「编译期常量」的固有属性,无法满足非类型模板参数必须是编译期确定值的要求。- 若要让这段代码编译通过,需要把参数改为编译期常量类型(比如
std::integral_constant<int, N>),或是直接将参数设为模板参数,确保其值在编译期可知。
内容的提问来源于stack exchange,提问作者Martin Fehrs
相关产品推荐
相关产品推荐

