Whyever **not** declare a function to be `constexpr`?
Solution 1
Functions can only be declared constexpr
if they obey the rules for constexpr
--- no dynamic casts, no memory allocation, no calls to non-constexpr
functions, etc.
Declaring a function in the standard library as constexpr
requires that ALL implementations obey those rules.
Firstly, this requires checking for each function that it can be implemented as constexpr
, which is a long job.
Secondly, this is a big constraint on the implementations, and will outlaw many debugging implementations. It is therefore only worth it if the benefits outweigh the costs, or the requirements are sufficiently tight that the implementation pretty much has to obey the constexpr
rules anyway. Making this evaluation for each function is again a long job.
Solution 2
I think what you're referring to is called partial evaluation. What you're touching on is that some programs can be split into two parts - a piece that requires runtime information, and a piece that can be done without any runtime information - and that in theory you could just fully evaluate the part of the program that doesn't need any runtime information before you even start running the program. There are some programming languages that do this. For example, the D programming language has an interpreter built into the compiler that lets you execute code at compile-time, provided that it meets certain restrictions.
There are a few main challenges in getting partial evaluation working. First, it dramatically complicates the logic of the compiler because the compiler will need to have the ability to simulate all of the operations that you could put into an executable program at compile-time. This, in the worst case, requires you to have a full interpreter inside of the compiler, making a difficult problem (writing a good C++ compiler) and making it orders of magnitude harder to do.
I believe that the reason for the current specification about constexpr
is simply to limit the complexity of compilers. The cases it's limited to are fairly simple to check. There's no need to implement loops in the compiler (which could cause a whole other slew of problems, like what happens if you get an infinite loop inside the compiler). It also avoids the compiler potentially having to evaluate statements that could cause segfaults at runtime, such as following a bad pointer.
Another consideration to keep in mind is that some functions have side-effects, such as reading from cin
or opening a network connection. Functions like these fundamentally can't be optimized at compile-time, since doing so would require knowledge only available at runtime.
To summarize, there's no theoretical reason you couldn't partially evaluate C++ programs at compile-time. In fact, people do this all the time. Optimizing compilers, for example, are essentially programs that try to do this as much as possible. Template metaprogramming is one instance where C++ programmers try to execute code inside the compiler, and it's possible to do some great things with templates partially because the rules for templates form a functional language, which the compiler has an easier time implementing. Moreover, if you think of the tradeoff between compiler author hours and programming hours, template metaprogramming shows that if you're okay making programmers bend over backwards to get what they want, you can build a pretty weak language (the template system) and keep the language complexity simple. (I say "weak" as in "not particularly expressive," not "weak" in the computability theory sense).
Hope this helps!
Solution 3
If the function has side effects, you would not want to mark it constexpr
. Example
I can't get any unexpected results from that, actually it looks like gcc 4.5.1 just ignores constexpr
Lars
Updated on June 06, 2022Comments
-
Lars almost 2 years
Any function that consists of a return statement only could be declared
constexpr
and thus will allow to be evaluated at compile time if all arguments areconstexpr
and onlyconstexpr
functions are called in its body. Is there any reason not to declare any such functionconstexpr
?Example:
constexpr int sum(int x, int y) { return x + y; } constexpr i = 10; static_assert(sum(i, 13) == 23, "sum correct");
Could anyone provide an example where declaring a function
constexpr
would do any harm?
Some initial thoughts:
Even if there should be no good reason for ever declaring a function not
constexpr
I could imagine that theconstexpr
keyword has a transitional role: its absence in code that does not need compile-time evaluations would allow compilers that do not implement compile-time evaluations still to compile that code (but to fail reliably on code that needs them as made explict by usingconstexpr
).But what I do not understand: if there should be no good reason for ever declaring a function not
constexpr
, why is not every function in the standard library declaredconstexpr
? (You cannot argue that it is not done yet because there was not sufficient time yet to do it, because doing it for all is a no-brainer -- contrary to deciding for every single function if to make itconstexpr
or not.) --- I am aware that N2976 deliberately not requires cstrs for many standard library types such as the containers as this would be too limitating for possible implementations. Lets exclude them from the argument and just wonder: once a type in the standard library actually has aconstexpr
cstr, why is not every function operating on it declaredconstexpr
?In most cases you also cannot argue that you may prefer not to declare a function
constexpr
simply because you do not envisage any compile-time usage: because if others evtl. will use your code, they may see such a use that you do not. (But granted for type trait types and stuff alike, of course.)So I guess there must be a good reason and a good example for deliberately not declaring a function
constexpr
?(with "every function" I always mean: every function that meets the requirements for being
constexpr
, i.e., is defined as a single return statement, takes only arguments of types with constexpr cstrs and calls onlyconstexpr
functions. Since C++14, much more is allowed in the body of such function: e.g., C++14 constexpr functions may use local variables and loops, so an even wider class of functions could be declaredconstexpr
.)The question Why does
std::forward
discardconstexpr
-ness? is a special case of this one. -
Ben Voigt about 13 yearsThe rules don't appear to forbid side-effects.
-
Matthieu M. about 13 yearsWhile a very interesting answer (partial evaluation is a big topic, and every compiler writer has tried it a bit), it seems a bit off-topic, since the question is more about the use of
constexpr
(for the programmer) than its effect, it seems. -
templatetypedef about 13 years@Matthieu M.- I may have misread the question. My interpretation of the question is "why not design
constexpr
so that any function can beconstexpr
and the compiler will give an error if it can't do it at compile-time?" In that sense, I think this answer is appropriate. If I'm way off from the OP's original question, I can delete this post. -
Matthieu M. about 13 yearsI'd rather you leave this post, it's interesting. I didn't know that D embedded an interpreter for example :) I must admit I am a bit fuzzy on the requirements for
constexpr
, I'll need to work on it. -
Lars about 13 years@Matthieu M. Thanks for the information. For me, too, the things about D were new and interesting. But Matthieu is right, you answer the question: why is constexpr limited to functions consisting of a single return statement? While my question is: will we now have to declare every function constexpr or are there any cases where this would have negative effects? (But please leave your answer!)
-
Lars about 13 yearsIn your example, f calls
operator++
on a non-constexpr variable g, and thus is not a valid constexpr function. g++ (snapshot 2011-02-19) correctly throws an error message: "g is not a constant expression". -
Lars about 13 yearsIt was not my intention to criticize the job of the library working group, sorry if my question should have sounded like that. What you say relates to interfaces, and you are right, the standard library describes such an interface with many implementations. So evtl. looking into the STL for guidance of constexpr usage was not clever by me. My question is about implementations. Would you agree that implementations should be constexpr whenever possible? Or are there examples for implementation not being constexpr? (Sorry, evtl. I did not get your point...)
-
Anthony Williams about 13 yearsMaking something
constexpr
is quite constraining. It is not something I would do lightly, as once it becomes part of your interface it is hard to change it due to backwards compatibility concerns. Even for a given implementation of the standard library this applies --- many people will unwittingly rely on implementation properties of the libraries they use, so I would not expect implementations to make any functionsconstexpr
apart from those required by the standard, or which there is no reasonable implementation which would violate the constraints. -
Ben Voigt about 13 years@Lars: I read the
[dcl.constexpr]
section of the standard (draft 3225) several times and couldn't find anything that prevents this function from beingconstexpr
, since the text "expression is a potential constant expression" got removed. -
Benjamin Bannier about 12 yearsThis is a very useful answer, and I agree on your point that TMP provides a very nice pure functional language. Although,
constexpr
allows writing pure functional code as well, while providing a nice bridge between runtime and compile-time variables. However, not having functions likestd::find
and some others definedconstexpr
seems to artificially limit the kinds of pure code one can write using the STL. -
Potatoswatter over 10 yearsNot sure how things were at the time this answer was written, but constexpr doesn't disqualify a function from having
dynamic_cast
ornew
. The constant-ness of an expression cannot be determined while it has unbound parameters, so there's no requirement that thereturn
expression be constant except as a potentiality. Since C++ compiler analysis doesn't deal in potentialities, there is no diagnostic required, i.e. nothing is verified. -
Anthony Williams over 10 yearsYou are right that the C++ Standard says "ill-formed; no diagnostic required" for violation of the
constexpr
requirements in the function definition. That doesn't make it OK --- it's still ill-formed if it cannot be part of a constant expression. If the non-constant-expression-suitable parts are dependent on the values of function parameters then it's only a hard error if you try and use it in a constant expression. Likewise for templates. -
Pedro Reis about 7 yearsHi! Sorry, still not understanding, could you give a code example? If we declare constexpr in a function and this may be or not constexpr after compiler evaluation, then why not declare it in every function that may compile this way (if we want to run more in compile time than in runtime)?
-
Anthony Williams about 7 yearsFirstly,
constexpr
functions are implicitlyinline
, so if you don't want to put your function definitions in your header files, they can't beconstexpr
. Secondly, declaring a functionconstexpr
means you cannot use try blocks,static
variables,thread_local
variables, or variables of non-literal types, so nostd::string
orstd::vector
, etc. -
Anthony Williams about 7 yearsIf you have an
inline
function that could beconstexpr
, there is no downside to declaring it as such, except that it might constrain you for the future --- changing the implementation to not beconstexpr
-compatible would then be an ABI break, as somewhere in your code it might be used in a constant expression. -
Alexander Malakhov over 5 years"Example" link is dead, ideone says "Solution not found"
-
Ben Voigt over 5 years@AlexanderMalakhov: Report it to ideone. Their FAQ guarantees that code is available "forever": ideone.com/faq I no longer use that site exactly because they delete content, without warning, and in violation of their own terms.
-
Alexander Malakhov over 5 yearsI didn't mean to say it's your fault :) But maybe you remember that code and could reproduce it here?