3
votes

In the following example, I use static_assert to verify that foo is determined at compile time. The static_assert passes and I've checked with an incorrect condition that it's actually active. This implies that foo is known at compile time. But if I step through the code with a debugger, I see that skip_first_word is also executed at run time.

// 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;
}

What's going on here? Why can't the foo that was calculated at compile-time be used at runtime?

Edit: This behavior is observed with Visual Studio 2015

1
You've used the same optimization level in both cases, right? - n.caillou
@n.caillou Yes, I've run both cases with and without optimizations. - François Andrieux
@FrançoisAndrieux: updated my answer to cover VS2015 behavior - Vittorio Romeo

1 Answers

5
votes

gcc.godbolt.org shows that your code is completely optimized out with both gcc 7 and clang 3.9, using the -std=c++11 -O1 flags.

Uncommenting the volatile operations will obviously generate assembly instructions, but no instructions are generated for skip_first_word or str_len.


You're correct regarding Visual Studio: using CL 19 on gcc.beta.godbolt.org shows that assembly is being generated for:

constexpr auto foo = skip_first_word("Hello, World!");
constexpr auto foo_size = str_len(foo);

This seems like a compiler implementation defect, as constexpr variables should and could be completely computed at compile-time. Additionally, the variables are being used inside a static_assert, which is guaranteed to be evaluated at compile-time. This seems to suggest that the compiler is unnecessarily generating assembly for skip_first_word and str_len, even though they are never used in a run-time context.

Manually inlining the code as follows...

static_assert(str_len(skip_first_word("Hello, World!")) == 6, "Wrong size");
static_assert(str_len(skip_first_word("Hello, World!")) != 7, "Wrong size");

...produces no extra assembly.