在编译时计算C字符串的长度.这真的是constexpr吗
Computing length of a C string at compile time. Is this really a constexpr?
我试图在编译时计算字符串文字的长度。为此,我使用以下代码:
#include <cstdio>
int constexpr length(const char* str)
{
return *str ? 1 + length(str + 1) : 0;
}
int main()
{
printf("%d %d", length("abcd"), length("abcdefgh"));
}
一切如预期,程序打印4和8。clang生成的汇编代码表明,结果是在编译时计算的:
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
我的问题是:标准是否保证length
函数在编译时进行评估
如果这是真的,那么编译时字符串文字计算的大门就为我打开了……例如,我可以在编译时计算哈希,还有更多。。。
常数表达式不能保证在编译时进行评估,我们只有一段来自C++标准草案5.19
常数表达式的非规范性引用,尽管如此:
[…]>[注意:常量表达式可以在翻译--尾注]
您可以将结果分配给constexpr
变量,以确保在编译时对其进行评估,我们可以从Bjarne Stroustrup的C++11参考中看到这一点,该参考中说(emphasis mine):
除了能够在编译时计算表达式之外,我们希望能够要求在编译时对表达式求值时间变量定义前面的constexpr会执行此操作(以及表示常量):
例如:
constexpr int len1 = length("abcd") ;
Bjarne Stroustrup在这个isocpp博客条目中总结了我们何时可以确保编译时评估,并说:
[…]正确答案-如前所述根据标准,constexpr函数可以除非用作常量表达式,在这种情况下,必须在编译时。为了保证编译时评估,我们必须使用它需要常量表达式(例如,作为数组绑定或作为大小写标签)或使用它来初始化constexpr。我希望没有一个自尊的编译器会错过优化有机会做到我最初所说的:"constexpr函数如果其所有参数都是常量,则在编译时进行求值表达式。"
因此,这概述了应该在编译时对其进行评估的两种情况:
- 在需要常量表达式的地方使用它,这似乎是标准草案中使用短语
shall be ... converted constant expression
或shall be ... constant expression
的任何地方,例如数组绑定 - 使用它来初始化
constexpr
,正如我上面概述的那样
很容易发现对constexpr
函数的调用是导致核心常量表达式还是只是在优化:
在需要常量表达式的上下文中使用它。
int main()
{
constexpr int test_const = length("abcd");
std::array<char,length("abcdefgh")> test_const2;
}
让我提出另一个函数,它在编译时计算字符串的长度,而不是递归的。
template< size_t N >
constexpr size_t length( char const (&)[N] )
{
return N-1;
}
请在ideone上查看此示例代码。
请注意,现代编译器(如gcc-4.x)在编译时对字符串文本执行strlen
,因为它通常被定义为一个内部函数。未启用任何优化。尽管结果不是编译时常数。
例如:
printf("%zun", strlen("abc"));
结果:
movl $3, %esi # strlen("abc")
movl $.LC0, %edi # "%zun"
movl $0, %eax
call printf
尽管任何合理的编译器都会在启用适当的优化级别时对constexpr
函数进行评估,但不能保证在编译时对其进行评估。另一方面,必须在编译时评估模板参数。
我使用以下技巧在编译时强制进行评估。不幸的是,它只适用于整数值(即不适用于浮点值)。
template<typename T, T V>
struct static_eval
{
static constexpr T value = V;
};
现在,如果你写
if (static_eval<int, length("hello, world")>::value > 7) { ... }
可以确信if
语句是一个编译时常数,没有运行时开销。
非常简单:
sizeof("myStringLiteral")
做了这项工作。
CCD_ 12内在编译器函数在编译时评估保证。它是一个强大的编译器功能,经常被低估。它适用于C++和C.
注意:您可能需要从size_t
转换为int,并减去1,这两项操作都是在编译时完成的:
int test_sizeof_text = (int)(sizeof("1234567")-1);
sizeof("text")
是包含终止0的大小,因此字符数为-1。
从C++20开始,您可以使用consteval
而不是constexpr来强制执行函数的编译时执行。
维基百科关于广义常数表达式的条目的简短解释:
在函数上使用constexpr会对该函数的功能造成一些限制。首先,该函数必须具有非void返回类型。其次,函数体不能声明变量或定义新类型。第三,主体可能只包含声明、null语句和单个返回语句。必须存在参数值,以便在参数替换之后,return语句中的表达式生成常量表达式。
在函数定义之前使用constexpr
关键字指示编译器检查是否满足这些限制。如果是,并且函数是用常量调用的,则返回的值保证是常量,因此可以在需要常量表达式的任何地方使用。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 多成员Constexpr结构初始化
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 在决定是通过参考还是通过价值时,尺寸真的是一个问题吗
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 为什么std::isnan 不是 constexpr?
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 更多constexpr容器是否需要mark_immutable_if_consexpr
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- constexpr上下文中std::initializer_list的验证
- 当迭代器(输入参数)通常不是constexpr时,constexpr算法真的有用吗
- C++constexpr函数真的能接受非常量表达式作为参数吗
- 在编译时计算C字符串的长度.这真的是constexpr吗
- 真的需要 constexpr 吗?