C++ 在编译时计算C字符串的长度。这真的是一个constexpr吗?

C++ 在编译时计算C字符串的长度。这真的是一个constexpr吗?,c++,c++11,standards,constexpr,string-literals,C++,C++11,Standards,Constexpr,String Literals,我试图在编译时计算字符串文字的长度。为此,我使用以下代码: #include <cstdio> int constexpr length(const char* str) { return *str ? 1 + length(str + 1) : 0; } int main() { printf("%d %d", length("abcd"), length("abcdefgh")); } 我的问题:标准是否保证编译时对length函数进行评估? 如果这是真的,

我试图在编译时计算字符串文字的长度。为此,我使用以下代码:

#include <cstdio>

int constexpr length(const char* str)
{
    return *str ? 1 + length(str + 1) : 0;
}

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}
我的问题:标准是否保证编译时对
length
函数进行评估?


如果这是真的,编译时字符串文本计算的大门就为我打开了。。。例如,我可以在编译时计算哈希值和更多

很容易发现对
constexpr
函数的调用是导致核心常量表达式,还是仅仅被优化:

在需要常量表达式的上下文中使用它

int main()
{
    constexpr int test_const = length("abcd");
    std::array<char,length("abcdefgh")> test_const2;
}
intmain()
{
constexpr int test_const=长度(“abcd”);
std::阵列测试常数2;
}

维基百科关于以下内容的简短解释:

对函数使用constexpr会对该函数的功能施加一些限制。首先,函数必须具有非void返回类型。其次,函数体不能声明变量或定义新类型。第三,主体可能只包含声明、null语句和单个返回语句。必须存在参数值,以便在参数替换后,return语句中的表达式生成常量表达式


constexpr
关键字放在函数定义之前会指示编译器检查是否满足这些限制。如果是,并且使用常量调用该函数,则返回值保证为常量,因此可以在需要常量表达式的任何地方使用。

不保证在编译时对
constepr
函数求值,尽管任何合理的编译器都会在适当的优化级别上执行此操作。另一方面,必须在编译时计算模板参数

int main()
{
    constexpr int test_const = length("abcd");
    std::array<char,length("abcdefgh")> test_const2;
}
我使用以下技巧在编译时强制求值。不幸的是,它只适用于整数值(即不适用于浮点值)

模板
结构静态评估
{
静态constexpr T值=V;
};
现在,如果你写信

if (static_eval<int, length("hello, world")>::value > 7) { ... }
if(static_eval::value>7){…}

您可以确保
if
语句是一个编译时常量,没有运行时开销。

常量表达式不保证在编译时进行计算,我们只有
5.19节
常量表达式中的一个非规范性引用,尽管如此:

[…]>[注意:常数表达式可以在 翻译-尾注]

您可以将结果分配给
constexpr
变量,以确保在编译时对其进行计算,我们可以从中看到(强调我的):

除了能够在编译时计算表达式之外,我们还可以 希望能够要求在编译时计算表达式 时间变量定义前面的constexpr执行该操作(以及 表示常数):

例如:

constexpr int len1 = length("abcd") ;
Bjarne Stroustrup总结了我们什么时候可以保证编译时评估,并说:

[…]正确答案-如上所述 由Herb-是指根据标准,constexpr函数可以 除非用作 常量表达式,在这种情况下,必须在 编译时。为了保证编译时评估,我们必须使用 当需要常量表达式时(例如,作为数组绑定或 或使用它初始化constexpr。我希望 任何自尊的编译器都不会错过优化 我最初说过:“constexpr函数是 如果其所有参数都是常量,则在编译时求值 表达方式。”

因此,这概括了两种情况,在编译时应该对其进行评估:

0x100000f5e:  leaq   0x35(%rip), %rdi          ; "%d %d"
0x100000f65:  movl   $0x4, %esi
0x100000f6a:  movl   $0x8, %edx
0x100000f6f:  xorl   %eax, %eax
0x100000f71:  callq  0x100000f7a               ; symbol stub for: printf
  • 如果需要一个常量表达式,则使用它,这似乎是标准草案中短语
    应为。。。转换后的常量表达式
    应为。。。使用常量表达式
    ,例如数组绑定
  • 使用它来初始化一个
    constepr
    ,正如我在上面概述的那样

  • 需要注意的是,现代编译器(如gcc-4.x)在编译时对字符串文本执行
    strlen
    ,因为它通常被定义为。没有启用优化。尽管结果不是编译时常量

    例如:

    结果:

    movl    $3, %esi    # strlen("abc")
    movl    $.LC0, %edi # "%zu\n"
    movl    $0, %eax
    call    printf
    

    让我提出另一个函数,它在编译时计算字符串的长度,而不是递归的

    template< size_t N >
    constexpr size_t length( char const (&)[N] )
    {
      return N-1;
    }
    
    模板
    常量大小\u t长度(字符常量(&)[N])
    {
    返回N-1;
    }
    

    请看一看。

    只要参数是一个常量表达式,它就必须是。@chris有没有保证当在不需要常量表达式的上下文中使用时,可以是常量表达式的东西必须在编译时进行计算?顺便说一句,包括
    然后调用
    ::printf
    是不可移植的。标准只要求
    提供
    std::printf
    @BenVoigt Ok,感谢您指出:)最初我使用std::cout,但是生成的代码非常大,无法找到实际值:)@Felics我在回答有关优化的问题时经常使用,使用
    printf
    可以大大减少需要处理的代码。。。。如果使用gcc,则使用
    -pedantic
    进行编译。否则,您将不会收到警告和警告errors@B或者在GCC没有可能妨碍的扩展(例如模板参数)的环境中使用它。enum hack不是更可靠吗?例如
    enum{Whatever=length(“str”)}
    ?值得一提的是
    static_断言(length(“str”)==3“”
    constexpr自动测试=/*…*/可能是最通用和最直接的。或者只使用std::integral_constant::value这个例子有点毫无意义
    
    template< size_t N >
    constexpr size_t length( char const (&)[N] )
    {
      return N-1;
    }