Hacker News new | past | comments | ask | show | jobs | submit login

>In conclusion, at the time of this writing, you need to inspect the generated code of your compiler, if you want to be sure that something is really calculated at compile time.

I don't get this. Why? Doesn't making a variable constexpr ensure it is a compile time value, or it will fail to compile?




> Doesn't making a variable constexpr ensure it is a compile time value

It ensures that the variable could be evaluated at compile time, where "could be evaluated at compile time" means "conforms to the rules set forth in the C++ standard". Whether or not it is computed at compile time is dependent on the compiler.


> It ensures that the variable could be evaluated at compile time

Why couldn't a variable without constexpr be evaluated at compile-time? Nobody ever explains this.


The option was discussed during standardization. It is expected that some corners of the language will never be available at compile time, for example I/O. So you need a keyword to declare a function to be callable a compile time, otherwise its constexpressness would depend on its implementation details and wouldn't be possible to check it in isolation.

It might not be a great reason (some code becomes a keyword soup with all the pointless boilerplate, we really need a DWIM[1] keyword).

[1] Declare With Implicit Meaning of course.


(EDIT to my last comment since I can't actually edit it -- I'm not sure if I misinterpreted your comment earlier or if you updated yours, but this is my updated reply.)

> It is expected that some corners of the language will never be available at compile time, for example I/O. So you need a keyword to declare a function to be callable a compile time, otherwise its constexpressness would depend on its implementation details and wouldn't be possible to check it in isolation.

Don't compilers already require the implementation of a function to be there in order to evaluate the code at compile-time? And don't they already have to verify that it can indeed be called at compile-time? I don't really get what the constexpr flag helps the compiler. It could just assume everything is constexpr implicitly unless proven otherwise.


constexpr isn't just a claim about today - it's a promise for the future. That is, the compiler can certainly notice that a function can be evaluated at compile time, and indeed optimizers will often perform enough inlining and constant propagation to boil down function calls to constant values in the binary. The constexpr keyword serves two purposes: it claims that the implementation is usable in constant expressions today (therefore allowing compilers to diagnose (i.e. emit compiler errors for) things that can't be done at compiletime, like I/O), and it promises that the implementation won't change in the future to be hostile to compile-time evaluation. This promise is important - otherwise, users could take a dependency on a function's current behavior (e.g. by using its result as an array bound, or as a template argument), and then they would be broken by implementation changes in the future that prohibited compile-time evaluation.


I see, so you're saying it's a verifiable promise by the function writer that it can be evaluated at compile-time?

Meaning its entire purpose is to just be an easier version of a compile-time unit-test like static_assert(f(1) == 2)?


Beautifully put. This is what's missing from D's approach.


> It is expected that some corners of the language will never be available at compile time, for example I/O.

So you're saying functions that have side-effects like I/O can actually perform those operations at compile-time if marked with constexpr?


No: the purpose of constexpr is that the compiler would issue an error if a constexpr function contains such side-effects.


No? Wouldn't it do that with a static_assert invoking the function too?


No sure I get the question: static_assert can only invoke constexpr functions, and constexpr functions can only call themselves other constexpr functions. Since IO functions are not constexpr, you can't call them there.


There's precedent in C++ for keywords that give the compiler hints it doesn't necessarily promise to act on: inline.


Shameless pedant kneejerk: 'inline' has semantics in additional to the optimization-hint. e.g. inlined functions won't trigger multiple-definition link-errors, even if they're left as ordinary functions in the executable.


Happy to be corrected (EDIT: indeed, I stand corrected! see replies below), but I don't think the C++ spec says anything about 'inline' encouraging the compiler to inline the compiled code?

'auto' would've been an example, but that also got removed, because they realized it was useless. I don't get how constexpr is any different.


I don't know my way around the spec, but Wikipedia is pretty unequivocal:

"... it serves as a compiler directive that suggests (but does not require) that the compiler substitute the body of the function inline by performing inline expansion, i.e. by inserting the function code at the address of each function call, thereby saving the overhead of a function call."

https://en.wikipedia.org/wiki/Inline_function


Thanks for this! I was very skeptical, but you made me look through every single mention of the word 'inline' in the spec, and you are indeed correct. :) Here is the relevant quote:

> [7.1.2] [dcl.fct.spec] A function declaration with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by shall still be respected


At first I was going to say that it's similar to asm.js - that it enabled an optimization but also could fall back to just running the code. However in trying to find references to back up my answer, I found a different explanation of constexpr which changed my own understanding.

https://stackoverflow.com/questions/38879475/why-cant-conste...

Quoting Ben Voigt: "What constexpr does is provide guarantees on what data-flow analysis a compliant compiler is required to do to detect1 compile-time-computable expressions, and also allow the programmer to express that intent so that they get a diagnostic if they accidentally do something that cannot be precomputed."


Apparently constexpr is like "inline". Just as the inline keyword doesn't actually guarantee that the compiler will inline a function, so constexpr doesn't require the compiler to precompute something, but rather just gives a hint to the compiler that you'd like it to if it can.


constexpr means you CAN use the function in a constant expression

inline means you CAN define a function in multiple translation units without violating ODR

no guarantees in the other direction


Gotcha, I think I understood it.


Literally the second example shows a failing case for Clang 5 where GCC is able to statically compute it at compile time.


That sounds like a bug in Clang?


It may not be a bug. The computation of Fibbonacci(10) requires a stack of ten frames. It may be that Clang has a limit on the depth of the stack for pre-compiled code.


For integral values you can use a little hack to ensure compile-time evaluation:

    namespace detail {
        template<class T, T val>
        constexpr T eval() { return val; }
    };

    #define FORCE_COMPILE_TIME(x) detail::eval<decltype(x), x>()


This is exactly why I don't like C++: it is just too complicated, and it encourages to write complicated code, usually opaque boilerplate template hacks for what would seem a simple concept. This is difficult to read, review, understand, and hence error-prone. No, not only error-prone, but bug-encouraging.

BTW, you can also enforce compile time evaluation like this:

   enum { aux1 = X };
   use(aux1);
But that's two lines, I know. And, of course, it is still a hack ('enum'? What?).


Interestingly D actually used `enum` to define compile time values (as opposed to `alias` which is used for types). If you want a compile time value you can do this:

    enum aux1 = X;


There might be a case where you expected the compiler to constexpr something but it didn't. I can imagine cases where a heavy function getting called more than once, because you had expected it to be pre-computed.


Oh look another flag that has several caveats and corner cases and works only half the times you think it works, while increasing compiler complexity

Here's a better idea, if you really need a compile time value in your program you calculate it yourself (or just run it at program startup and cache the value).


Or the compiler could behave intuitively, but that's hard in C++ due to all of the past decisions that need to be supported.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: