Hacker News new | ask | show | jobs
by andreidd 1521 days ago
No, it can't. std::to_string isn't constexpr. And even if it was, it still wouldn't work because the std::string needs to be destroyed inside the constexpr context.

The article is also wrong because std::to_chars isn't constexpr so you can't use that.

1 comments

Compiler Explorer is your friend, https://godbolt.org/z/94hzK8svE

In one thing you're actually right, I should have used a string_view for the return value instead.

    #include <vector>
    #include <string>
    #include <algorithm>
    #include <string>
    #include <cstdio>

    constexpr std::string_view get_fizzbuzz(int number) {
        if (number % 15 == 0) {
            return "FizzBuzz";
        } else if (number % 3 == 0) {
            return "Fizz";
        } else if (number % 5 == 0) {
            return "Buzz";
        }
        return std::to_string(number); // convert to string
    }

    int main() {
        static constexpr auto value = get_fizzbuzz(15);
        puts(value.data());
    }
The template metaprogramming to expand all values for get_fizzbuzz() is left as exercise.
Your example only works because the optimizer eliminates your call to std::to_string.

Call get_fizzbuzz(11) and you'll see the error.

I stand corrected, however I have a couple of ideas to try later on.
You’re gonna have a bad time if you return a string_view to a locally created string.
String literals are specified to have static storage duration. That means references are valid at least until main returns. In practice string literals are immortal and references to them are always valid.
I didn’t say string literal. I said “locally created string” i.e. std::string

In particular, my comment was referring to “return std::to_string(number);”

I missed that. You're right.
It is compile time, so the expectation is that the compiler replaces it anyway, although you might be right.
`constexpr` isn't "compile time". It's potentially compile time at best. Debug builds in particular will go out of their way to evaluate things at runtime, presumably so you can set breakpoints and step through code, even when it's completely pointless. I have seen the following function show up in a profiler:

    template < bool value >
    bool constexpr IsEnabled() { return value; }
This was used to silence warnings about unconditional branches in a macro. There are ways to force such functions to be evaluated at compile time, but they're pretty awkward, and limited to integral types (you can't use them on string_view s):

    #define EVAL_AT_COMPILE_TIME(x) std::integral_constant<decltype(x), x>::value
    const bool x = EVAL_AT_COMPILE_TIME(IsEnabled<true>());
> There are ways to force such functions to be evaluated at compile time, but they're pretty awkward

C++20 has “consteval“: https://en.cppreference.com/w/cpp/language/consteval

That is why main() has a static constexpr, to force its execution at compile time. A trick I learned from Jason's screencasts.

Although, you're right. I tried it back at home on VC++ and its static analyser wasn't happy.

My idea was to make use of template metaprogramming to generate the string buffer, there are a couple of examples of such attempts.

However, I guess with such amount of additional code, I should declare defeat on the original comment.

Seems to me the good news is that your compiler told you this doesn't work.

In languages where you can't tell the compiler you think this is constant, there's a risk you delude yourself, especially because computers are very fast, and you think you've got O(1) when it's actually O(N) or even O(N^2) and one day N gets big enough and you're in real trouble.

Have to defer conversion to std string to runtime https://godbolt.org/z/rWKEr4P6h
Yeah, I was wrong on that regard.

Although I still think with a mix of template metaprogramming and constexpr might be possible, however it would be a very low ROI on such example.

And it would still be worse than the other languages, so defeat accepted.

I'm not sure deferred conversion is a bad thing in this toy instance.

I agree that being able to easily tradeoff between time and space is an important property of compile time evaluation and C++ doesn't make that easy with the no escaped allocations requirement.

On the other hand, I shudder to think what will happen to binary size. It can't be any worse than codegenning all the things with reckless abandon... Right?

>The template metaprogramming to expand all values for get_fizzbuzz() is left as exercise.

There's some joke here about authors using "left as exercise to the reader" to skip debugging their broken code.

Or like I don't need to fully implement it for the next interview.