在以下示例中,我用于static_assert
验证是否foo
在编译时确定.该static_assert
通行证和我有一个不正确的条件下,它实际上是积极的检查.这意味着foo
在编译时已知.但是,如果我使用调试器逐步执行代码,我会看到它skip_first_word
也在运行时执行.
// Skip the first word in p_str constexpr const char * skip_first_word(const char * p_str) { return (*p_str == '\0') ? p_str : (*p_str == ' ') ? p_str + 1 : skip_first_word(p_str + 1); } // constexpr to calculate the length of a string constexpr size_t str_len(const char * p_str) { return (*p_str == '\0') ? 0 : str_len(p_str + 1) + 1; } int main() { constexpr auto foo = skip_first_word("Hello, World!"); constexpr auto foo_size = str_len(foo); static_assert(foo_size == 6, "Wrong size"); // This assert successfully fails // static_assert(foo_size == 7, "Wrong size"); // Prevent optimizations for(auto ptr = foo; *ptr != '\0'; ++ptr) { volatile auto sink = ptr; } volatile auto sink = &foo_size; return 0; }
这里发生了什么?为什么不能foo
在运行时使用在编译时计算的那个?
编辑:使用Visual Studio 2015观察到此行为
gcc.godbolt.org显示您的代码使用标志完全使用gcc 7和clang 3.9进行了优化-std=c++11 -O1
.
取消注释volatile
操作显然会生成汇编指令,但不会为skip_first_word
或生成指令str_len
.
关于Visual Studio你是正确的:在gcc.beta.godbolt.org上使用CL 19表明正在为以下内容生成程序集:
constexpr auto foo = skip_first_word("Hello, World!"); constexpr auto foo_size = str_len(foo);
这看起来像编译器实现缺陷,因为constexpr
变量应该并且可以在编译时完全计算.另外,变量在a中使用static_assert
,保证在编译时进行评估.这似乎表明,编译器产生不必要的装配skip_first_word
和str_len
,即使他们从来没有在运行时环境中使用.
手动内联代码如下...
static_assert(str_len(skip_first_word("Hello, World!")) == 6, "Wrong size"); static_assert(str_len(skip_first_word("Hello, World!")) != 7, "Wrong size");
......没有产生额外的装配.