能否通过返回const对象强制含非const数组的constexpr函数编译期求值?
答案是:不会——仅仅将返回对象声明为const并不会强制calculations函数在编译期完成计算。下面来拆解原因和解决方法:
核心误区:const vs constexpr
首先要明确这两个关键字的本质区别:
const仅表示变量不可被修改,但它的值完全可以是运行期才确定的;constexpr才是强制要求变量是编译期可计算的常量表达式,这才会触发constexpr函数的编译期求值逻辑。
你的代码里const auto obj2 = ...只是让obj2不可修改,但并没有要求它必须是编译期常量,所以编译器完全可以选择在运行期执行calculations函数。
constexpr函数的求值触发条件
constexpr函数的求值时机完全取决于它的使用上下文,只有当它被用于以下场景时,才会被强制要求在编译期求值:
- 初始化
constexpr变量; - 作为模板参数传递;
- 定义数组的大小;
- 初始化枚举成员;
- 作为
static_assert的参数等其他需要常量表达式的场景。
回到你的代码,calculations的调用上下文只是初始化一个const变量,不属于上述强制编译期求值的场景。
如何强制编译期求值?
如果你想让calculations必须在编译期完成计算,需要做两个关键修改:
1. 将obj2声明为constexpr
把main里的obj2声明改成:
constexpr auto obj2 = calculations<double, size>(obj1);
这会明确告诉编译器:obj2必须是编译期常量,因此必须在编译期执行calculations函数。
2. 确保函数内部操作符合常量表达式要求
你的代码里还有一个隐藏问题:Array的构造函数不是constexpr,即使你把obj2改成constexpr,编译器也无法在编译期执行Example的构造函数(因为它会调用Array的非constexpr构造函数)。所以需要给Array的构造函数加上constexpr关键字:
template<int Size, typename T> struct Array{ T array[Size]; // 添加constexpr修饰构造函数 constexpr Array(const T * a){ for(int i = 0; i < Size; i++){ array[i] = a[i]; } } };
做完这两个修改后,calculations就会被强制在编译期求值了。
补充:关于函数内的非const数组
你提到函数内有非const数组D test1[2];,这在C++14及以后的constexpr函数里是允许的——constexpr函数允许修改自动存储期的变量,只要这些操作符合常量表达式的规则。所以这个数组本身不会阻碍编译期求值,真正的关键还是调用上下文是否要求常量表达式。
内容的提问来源于stack exchange,提问作者M.Mac

