C++ 为什么我们需要将函数标记为constexpr?

C++ 为什么我们需要将函数标记为constexpr?,c++,c++11,constexpr,C++,C++11,Constexpr,C++11允许使用constepr说明符声明的函数在常量表达式(如模板参数)中使用。对允许的内容有严格的要求constexpr;本质上,这样一个函数只封装一个子表达式,而不封装其他任何东西。(编辑:这在C++14中是宽松的,但问题仍然存在。) 为什么需要关键字呢获得了什么? 它确实有助于揭示接口的意图,但它通过保证函数在常量表达式中可用来验证该意图。编写constexpr函数后,程序员仍必须: 编写一个测试用例,或者确保它实际用于常量表达式中 记录哪些参数值在常量表达式上下文中有效 与揭示意图相

C++11允许使用
constepr
说明符声明的函数在常量表达式(如模板参数)中使用。对允许的内容有严格的要求
constexpr
;本质上,这样一个函数只封装一个子表达式,而不封装其他任何东西。(编辑:这在C++14中是宽松的,但问题仍然存在。)

为什么需要关键字呢获得了什么?

它确实有助于揭示接口的意图,但它通过保证函数在常量表达式中可用来验证该意图。编写
constexpr
函数后,程序员仍必须:

  • 编写一个测试用例,或者确保它实际用于常量表达式中
  • 记录哪些参数值在常量表达式上下文中有效
  • 与揭示意图相反,使用
    constexpr
    装饰函数可能会增加错误的安全感,因为在忽略中心语义约束的情况下会检查相切的语法约束



    简言之:如果函数声明中的
    constexpr
    仅仅是可选的,会不会对语言产生不良影响?或者对任何有效的程序都有影响吗?

    没有关键字,编译器无法诊断错误。编译器将无法告诉您该函数在语法上是无效的
    constexpr
    。虽然你说这会带来“虚假的安全感”,但我认为最好尽早发现这些错误。

    我们可以不使用
    constexpr
    ,但在某些情况下,这会使代码更简单、更直观。
    例如,我们有一个类,它声明了一个具有某个引用长度的数组:

    template<typename T, size_t SIZE>
    struct MyArray
    {
      T a[SIZE];
    };
    
    现在看看它与
    constexpr
    的关系:

    template<typename T, size_t SIZE>
    constexpr
    size_t getSize (const T (&a)[SIZE]) { return SIZE; }
    
    int a1[100];
    MyArray<decltype(*a1), getSize(a1)> obj;
    
    防止客户端代码期望超出您的承诺 假设我正在编写一个库,其中有一个函数当前返回一个常量:

    awesome_lib.hpp

    如果不需要
    constexpr
    ,作为客户机代码的作者,您可能会离开并执行以下操作:

    客户端应用程序cpp

    #包括
    #包括
    std::array my_array;//需要CT模板参数
    int my_c_数组[f()];//需要CT阵列尺寸
    
    然后,如果我将
    f()
    更改为从配置文件返回值,您的客户机代码将被破坏,但我不知道我有破坏您的代码的风险。实际上,只有当您遇到一些生产问题并重新编译时,您才会发现这个额外的问题阻碍了您的重建

    通过只更改
    f()
    的实现,我就可以有效地改变接口的用法

    相反,C++11以后的版本提供了
    constexpr
    ,因此我可以指出,客户机代码可以对保持
    constexpr
    的函数有合理的期望,并且可以这样使用它。我知道并认可这种用法作为我的界面的一部分。就像在C++03中一样,编译器继续保证客户端代码不会依赖于其他非
    constexpr
    函数来防止上面的“不需要的/未知的依赖”场景;这不仅仅是文档,而是编译时强制执行

    值得注意的是,这继续提供C++的趋势,为传统的预处理器宏的使用提供更好的替代方案(考虑代码< >定义f 4 < /C>),以及客户端程序员如何知道lib程序员是否认为它是公平的游戏来改变为“代码”>定义f配置([ f ] ] /代码>,以其众所周知的“邪恶”。例如在语言的名称空间/类范围系统之外

    为什么没有“显然”never const函数的诊断? 我认为这里的混乱是由于
    constepr
    没有主动确保有任何一组参数的结果实际上是编译时常量:相反,它要求程序员对此负责(否则标准中的§7.1.5/5认为程序格式不正确,但不要求编译器发出诊断)。是的,这很不幸,但它没有删除上述
    constexpr
    实用程序

    这样,也许有助于从“代码的什么地方> CONEXPRPR < /代码>的角度来考虑,“为什么我可以编译一个不能永远返回const值的代码< CONTXPRPR <代码>函数?” 答:因为需要进行详尽的分支分析,这种分析可能涉及任意数量的组合。诊断可能会在编译时和/或内存方面花费过多,甚至超出任何可想象的硬件的能力。此外,即使在实际情况下,也必须准确地诊断这种情况,这是一个全新的难题编译器编写者的rms(他们对自己的时间有更好的利用)。这也会对程序产生影响,例如从

    constexpr
    函数中调用的函数的定义需要在执行验证时可见(以及函数调用的函数等)

    同时,缺少
    constepr
    继续禁止将其用作常量值:严格性在sans-
    constepr
    方面。如上文所示,这很有用

    与非'const'成员函数的比较
    • constepr
      防止
      int x[f()]
      而缺少
      const
      则防止
      const x;x.f();
      -它们都确保客户端代码不会硬编码不需要的依赖关系

    • 在这两种情况下,您不希望编译器自动确定
      const[expr]
      -ness

      • 当您已经可以预见到一个
        const
        对象上的成员函数将演变为修改可观察值breaki时,您不会希望客户端代码调用该对象上的成员函数
        template<typename T, size_t SIZE>
        constexpr
        size_t getSize (const T (&a)[SIZE]) { return SIZE; }
        
        int a1[100];
        MyArray<decltype(*a1), getSize(a1)> obj;
        
        int i = 5;
        const int j = i; // ok, but `j` is not at compile time
        constexprt int k = i; // error
        
        inline int f() { return 4; }
        
        #include <awesome_lib.hpp>
        #include <array>
        
        std::array<int, f()> my_array;   // needs CT template arg
        int my_c_array[f()];             // needs CT array dimension