You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何constexpr函数中可使用lambda返回值作为常量表达式?

C++常量表达式中Callable参数的编译差异问题

代码示例

#include <array>

template <typename Callable>
constexpr auto make_array_ok(Callable callable) {
    return std::array<int, callable()>{};
};
// constexpr auto make_array_bad(std::size_t s)
// {
//     return std::array<int,s>{};
// };

// 直接返回参数的constexpr函数
template <std::size_t S>
constexpr std::size_t foo() {
    return S;
}

int main(int argc, char**) {
    static_cast<void>(argc);

    auto size = []() { return std::size_t{42}; };

    // 编译失败——符合预期
    // auto size_dyn = [argc]() { return std::size_t(argc); };
    // [[maybe_unused]] auto a = make_array_ok(size_dyn);

    // 同样编译失败——原因何在?
    [[maybe_unused]] auto size_capt = [arg = size()]() { return arg; };
    // [[maybe_unused]] auto b = make_array_ok(size_capt);
    // 同样编译失败——原因何在?
    [[maybe_unused]] constexpr auto size_capt_ce = [arg = size()]() {
        return arg;
    };
    // [[maybe_unused]] auto c = make_array_ok(size_capt_ce);

    // 直接调用也编译失败——是否因闭包非constexpr?当闭包为constexpr时直接调用可编译
    // [[maybe_unused]] auto d = std::array<int,size_capt()>{},
    [[maybe_unused]] auto e = std::array<int, size_capt_ce()>{},
    // 用constexpr函数替代lambda的简单示例
    [[maybe_unused]] auto f = std::array<int, foo<42>()>{},
    // 通过函数调用constexpr函数编译失败——原因何在?
    // [[maybe_unused]] auto g = make_array_ok(foo<42>);

    // 无捕获lambda可正常编译
    [[maybe_unused]] auto h = make_array_ok(size);
    return size_capt_ce();
}

核心疑问与解析

1. 无捕获lambda可编译,普通参数却不行的原因

无捕获lambda的operator()在C++17及以后默认是constexpr,且其行为完全编译期可知,没有运行时依赖。当传入模板函数make_array_ok时,编译器在模板实例化阶段能确定callable()的结果是编译期常量。

而make_array_bad的参数是std::size_t s,即使函数是constexpr,形参s本身不属于常量表达式的允许范畴——constexpr函数可被运行时调用,编译器无法保证s一定是编译期常量,因此不能用于std::array的模板参数。

2. 带捕获lambda传入make_array_ok编译失败的原因

带捕获lambda本质是带有成员变量的闭包类,当闭包对象作为形参传入make_array_ok时,callable()的调用依赖于这个形参对象。在常量表达式上下文里,函数形参(即使传入的是constexpr对象)的成员访问或方法调用是不被允许的——编译器无法将形参视为“常量表达式中的核心实体”,因此无法用其结果作为std::array的大小。

而直接调用size_capt_ce()时,size_capt_ce是constexpr变量,其方法调用直接在编译期计算,不经过函数形参传递,因此符合常量表达式要求。

3. 模板函数foo作为参数编译失败的原因

把foo<42>传给make_array_ok时,callable是函数指针形参。在常量表达式中,函数形参本身的不确定性(理论上可传入其他函数指针)导致编译器无法保证callable()的结果是编译期常量,因此无法用于模板参数。而直接调用foo<42>()时,编译器能直接确定结果为编译期常量,所以合法。

关键规则总结

在C++常量表达式上下文里:

  • 函数形参(除非是C++20的constexpr形参)不能用于生成编译期常量。
  • 无捕获lambda因行为完全编译期可知,其调用在模板实例化时可被确定为常量,因此能通过编译。
  • 带捕获闭包对象、函数指针作为形参时,其调用依赖于形参本身,不符合常量表达式的核心要求,无法用于模板参数。

内容的提问来源于stack exchange,提问作者Oersted

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 00:27:07