constexpr! and reduced code reusability.

366 views
Skip to first unread message

picpps...@gmail.com

unread,
Aug 6, 2018, 8:15:58 AM8/6/18
to ISO C++ Standard - Future Proposals
Hello,

Referring to the p1073r1 I was thinking about whether there makes sense in reducing resuability of code in order to be sure to execute the code in compile-time.

This seems to not be an issue at all, when there is constexpr! and non-constexpr! function defined, because we can provide optimized version of the code for runtime and 
compile-time version for compile-time context. 

I guess there still might be a use case of executing constexpr! function when it's non-constexpr! twin is not defined at all.

For this reason I was thinking about the other approach on enforcing compile-time execution, which would be present in calling syntax.

So with some example syntax:

constexpr(true) int foo(int);
constexpr(false) int foo(int); // optimized version for nonconstexpr contexts. MUST be present if constexpr(true) version is present for the sake of symmetry

or

constexpr void bar(int); //casual constexpr function

,so

foo(); // in constexpr contexts it's compile-time call in non-constexprs contexts it's call to optimized version of function.
constexpr foo(); // in constexpr contexts it's compile-time call in non-constexprs contexts it's ill formed.

bar(); // casual call according to the current C++ spec.
constexpr bar(); // in constexpr contexts it's compile-time call in non-constexprs contexts it's ill formed.

What do you think about it? Do you find this useful to not forbid calling constexpr! functions in non-constexpr contexts?

Nicol Bolas

unread,
Aug 6, 2018, 10:47:25 AM8/6/18
to ISO C++ Standard - Future Proposals
On Monday, August 6, 2018 at 8:15:58 AM UTC-4, picpps...@gmail.com wrote:
Hello,

Referring to the p1073r1 I was thinking about whether there makes sense in reducing resuability of code in order to be sure to execute the code in compile-time.

This seems to not be an issue at all, when there is constexpr! and non-constexpr! function defined, because we can provide optimized version of the code for runtime and 
compile-time version for compile-time context. 

I guess there still might be a use case of executing constexpr! function when it's non-constexpr! twin is not defined at all.

If the writer of that function did not provide a non-`constexpr` overload, then they do not want that function to be called outside of constant expression contexts. Maybe it's simply not meaningful to call it then. Maybe there's no real point to it. Maybe the function exists solely to do type computation, and the function therefore only needs to be "executed" for the purpose of `decltype` statements or other similar constant expression contexts.

Whatever the case, if a user has decided that this function should only be executed at compile time, then we should honor that decision.

I don't understand the problem you're trying to solve. You seem to be adding complexity for no good reason.

Dawid Pilarski

unread,
Aug 6, 2018, 1:47:58 PM8/6/18
to std-pr...@isocpp.org
Maybe the function exists solely to do type computation

In fact I haven't thought of type computations (usually doing it with structures, but you're right). Thanks for pointing this out.


I don't understand the problem you're trying to solve

Just to explain myself. I thought, that the primary issue, that p1073r1 was trying to solve is to provide the mechanism to be able to know for sure, that given function will be executed in compile time.
I thought, that instead of forbidding a function to be called at runtime at the definition, maybe it's better to introduce some calling syntax, that will help developer to force compile error if constexpr function is to be ran in runtime.

As there is good use case for forbidding constexpr function to be called  in runtime I find post of mine to be not relevant anymore. Thank you Nicolas for your time.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d37bf67a-0a14-4143-b09a-87fe96b6f90b%40isocpp.org.

Nicolas Lesser

unread,
Aug 6, 2018, 2:24:27 PM8/6/18
to std-pr...@isocpp.org
I don't like this idea.

constexpr!, or immediate functions, are proposed because they are useful for reflections, where you might have to have a function that can only be called at compile-time because it does some reflection stuff. constexpr is not enough for it, as a constexpr function can also be called with runtime arguments, and you can't reflect on runtime stuff. Now you're saying to lift this requirement? That would mean that there would be no difference between constexpr and immediate functions. Also, I don't really like "constexpr overloading" because a constexpr function can also be called at runtime, there is no inherit reason to think that a constexpr function should only be called at runtime; I think a better idea for this is std::is_constant_evaluated() (P0595).

I thought, that instead of forbidding a function to be called at runtime at the definition, maybe it's better to introduce some calling syntax, that will help developer to force compile error if constexpr function is to be ran in runtime.

No. As already said, immediate functions cannot be called at runtime if they use for example reflection stuff. Putting that requirement on the caller is a bit weird; that would mean that every call to an immediate call would have to be prefixed by `constexpr`? 

But I also don't see the motivation for allowing such a constexpr call syntax for constexpr functions. What's the motivation for this? 

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Aug 6, 2018, 4:23:35 PM8/6/18
to ISO C++ Standard - Future Proposals
On Monday, August 6, 2018 at 2:24:27 PM UTC-4, Nicolas Lesser wrote:
I don't like this idea.

constexpr!, or immediate functions, are proposed because they are useful for reflections, where you might have to have a function that can only be called at compile-time because it does some reflection stuff. constexpr is not enough for it, as a constexpr function can also be called with runtime arguments, and you can't reflect on runtime stuff. Now you're saying to lift this requirement? That would mean that there would be no difference between constexpr and immediate functions. Also, I don't really like "constexpr overloading" because a constexpr function can also be called at runtime, there is no inherit reason to think that a constexpr function should only be called at runtime; I think a better idea for this is std::is_constant_evaluated() (P0595).

I see no reason why we shouldn't have both `constexpr!` with overloading and `is_constant_evaluated`.

If the implementations of the two functions are going to be fundamentally different, I'd rather write them as two separate functions and let overloading do its job than to have to write a `constexpr` function that wraps the two implementations in an `if constexpr`. It's more self-documenting that way.

By contrast, there can be times when the non-constexpr version is the same as the constexpr one, except that maybe it does some file logging. So doing an `if constexpr(!is_constant_evaluated())` to surround such blocks is perfectly valid.

So long as the overloading rules are pretty obvious, there isn't really a problem with `constexpr!` overloading.

Ville Voutilainen

unread,
Aug 6, 2018, 4:49:25 PM8/6/18
to ISO C++ Standard - Future Proposals
On 6 August 2018 at 23:23, Nicol Bolas <jmck...@gmail.com> wrote:
> By contrast, there can be times when the non-constexpr version is the same
> as the constexpr one, except that maybe it does some file logging. So doing
> an `if constexpr(!is_constant_evaluated())` to surround such blocks is
> perfectly valid.

That condition is always false, so I don't know what you're getting at.

Arthur O'Dwyer

unread,
Aug 6, 2018, 6:26:50 PM8/6/18
to ISO C++ Standard - Future Proposals
Yes you do.


There's an unfortunate tendency of several people in "constexpr" discussions to treat the accidental behavior of

    if constexpr(is_constant_evaluated()) {
        do one thing
    } else {
        do another (but this code is in fact never executed, ha ha!)
    }

as a gotcha to be used as a source of triumph over the unenlightened masses, rather than a sad pothole in need of fixing up.

–Arthur

Ville Voutilainen

unread,
Aug 6, 2018, 6:41:51 PM8/6/18
to ISO C++ Standard - Future Proposals
On 7 August 2018 at 01:26, Arthur O'Dwyer <arthur....@gmail.com> wrote:
> On Monday, August 6, 2018 at 1:49:25 PM UTC-7, Ville Voutilainen wrote:
>>
>> On 6 August 2018 at 23:23, Nicol Bolas <jmck...@gmail.com> wrote:
>> > By contrast, there can be times when the non-constexpr version is the
>> > same
>> > as the constexpr one, except that maybe it does some file logging. So
>> > doing
>> > an `if constexpr(!is_constant_evaluated())` to surround such blocks is
>> > perfectly valid.
>>
>> That condition is always false, so I don't know what you're getting at.
>
>
> Yes you do.
>
>
> There's an unfortunate tendency of several people in "constexpr" discussions
> to treat the accidental behavior of

That behavior is not accidental.

bastie...@gmail.com

unread,
Aug 6, 2018, 6:55:26 PM8/6/18
to ISO C++ Standard - Future Proposals
Yes it's worse, it's confusing by design.
I don't see that being a factor to the "simple language trying to breakthrough".
I've already seen that "not accidental" behavior being misinterpreted and by people who have a pretty good understanding and long time experience of C++.
I don't see that stopping anytime soon.
std::is_constant_evaluated() is confusing because the only thing it does is that it has compile time side effects on simple if, turning them into bastardized version of "if constexpr" but without the interesting part of "if constexpr".
It is logical and normal that the user would then try to combine it with an "if constexpr" when what they want is to specialize.
When the "static if" conversation arise wasn't the argument that we didn't need it because we could use concepts to specialize?
And now that "static if" was trampled over to become "if constexpr" we want a weird intermediate between "if" and "if constexpr" through a built-in function to avoid allowing the user to specialize constexpr-ness?
Good to know.

Nicol Bolas

unread,
Aug 6, 2018, 8:51:41 PM8/6/18
to ISO C++ Standard - Future Proposals


On Monday, August 6, 2018 at 6:55:26 PM UTC-4, bastie...@gmail.com wrote:


On Tuesday, August 7, 2018 at 12:41:51 AM UTC+2, Ville Voutilainen wrote:
On 7 August 2018 at 01:26, Arthur O'Dwyer <arthur....@gmail.com> wrote:
> On Monday, August 6, 2018 at 1:49:25 PM UTC-7, Ville Voutilainen wrote:
>>
>> On 6 August 2018 at 23:23, Nicol Bolas <jmck...@gmail.com> wrote:
>> > By contrast, there can be times when the non-constexpr version is the
>> > same
>> > as the constexpr one, except that maybe it does some file logging. So
>> > doing
>> > an `if constexpr(!is_constant_evaluated())` to surround such blocks is
>> > perfectly valid.
>>
>> That condition is always false, so I don't know what you're getting at.
>
>
> Yes you do.
>
>
> There's an unfortunate tendency of several people in "constexpr" discussions
> to treat the accidental behavior of

That behavior is not accidental.
 
Yes it's worse, it's confusing by design.

It's rather like the Most Vexing Parse. MVP is based on an unfortunate combination of the ambiguity between a possible function declaration and a variable declaration initialized via constructor-call syntax. We can all understand why it's there, but we all would rather it not be there.

Only this time, the ambiguity is discovered before the feature became standard. So... why are we still doing it that way?

In fact, looking over P0595, it's actually negatively useful as a feature. It is only allowed to evaluate to "true" in cases where the expression is required to be a constant expression; it must evaluate to "false" in other cases, even if the compiler wants to run it at compile time. So you cannot use it for cases where there is a divergence between efficient the compile-time and runtime solutions.

So what good is the feature at all?

Ville Voutilainen

unread,
Aug 7, 2018, 1:41:17 AM8/7/18
to ISO C++ Standard - Future Proposals
On 7 August 2018 at 03:51, Nicol Bolas <jmck...@gmail.com> wrote:
> In fact, looking over P0595, it's actually negatively useful as a feature.
> It is only allowed to evaluate to "true" in cases where the expression is
> required to be a constant expression; it must evaluate to "false" in other
> cases, even if the compiler wants to run it at compile time. So you cannot
> use it for cases where there is a divergence between efficient the
> compile-time and runtime solutions.
>
> So what good is the feature at all?

It gives you a portable result, as opposed to a result that depends on
whether your implementation
folds or not. Just use a normal if instead of if constexpr and it'll
do what you want.

Dawid Pilarski

unread,
Aug 7, 2018, 1:53:14 AM8/7/18
to std-pr...@isocpp.org
@Nicolas Lesser


 What's the motivation for this? 

I will try to share with you my motivation.

there can be some numerical computation done in the constexpr function. For me it's important, that in some usages it will be compile time evaluated, otherwise I cannot fulfill my agreement with the user regarding performance.

So now I want to call it 

//context
my_contexpr_function_here(/*args*/);
//context

issues:
1. There is no way of checking whether the function really got executed in compile time,
2. There is no way if informing the reader (instead of the comment) to let him know that he must not break the compile-time evaluation of constexpr function.

So in my case after writing things down, to be sure, that function is compile-time evaluated I needed to check the generated code.

So, when I do:

constexpr my_contexpr_function_here(/*args*/);

when I do change in the code, I am sure, that if I break compile-time evaluation I will get compile time error.


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Nicolas Lesser

unread,
Aug 7, 2018, 2:28:01 AM8/7/18
to std-pr...@isocpp.org
Oh ok. Well, in that case, I would do:

constexpr auto result = foo(/*args*/);

so that foo is evaluated at compile-time. There's some talk about adding a constexpr compound statement, where everything in it would be constant evaluated so that would also solve your problem in a way (you might want to look into that). 

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Richard Hodges

unread,
Aug 7, 2018, 6:28:28 AM8/7/18
to std-pr...@isocpp.org
On Tue, 7 Aug 2018 at 07:28, Nicolas Lesser <blitz...@gmail.com> wrote:
Oh ok. Well, in that case, I would do:

constexpr auto result = foo(/*args*/);

so that foo is evaluated at compile-time. There's some talk about adding a constexpr compound statement, where everything in it would be constant evaluated so that would also solve your problem in a way (you might want to look into that). 

Since lambdas are already constexpr by default in c++17 it seems to me that the language already provides constexpr compound statements, no?

int main()
{
  constexpr auto x = []
  {
    int a= 10;
    a *= 2;
    a = a + 5;
    return a;
  }();

  return x;
}

 

Nicol Bolas

unread,
Aug 7, 2018, 11:18:32 AM8/7/18
to ISO C++ Standard - Future Proposals


On Tuesday, August 7, 2018 at 1:53:14 AM UTC-4, Dawid Pilarski wrote:
@Nicolas Lesser

 What's the motivation for this? 

I will try to share with you my motivation.

there can be some numerical computation done in the constexpr function. For me it's important, that in some usages it will be compile time evaluated, otherwise I cannot fulfill my agreement with the user regarding performance.

So now I want to call it 

//context
my_contexpr_function_here(/*args*/);
//context

issues:
1. There is no way of checking whether the function really got executed in compile time,
2. There is no way if informing the reader (instead of the comment) to let him know that he must not break the compile-time evaluation of constexpr function.

`constexpr!` handles both of those. You cannot call a `constexpr!` function with non-constexpr parameters, and the return value is always `constexpr`.

So in my case after writing things down, to be sure, that function is compile-time evaluated I needed to check the generated code.

`constexpr!` ensures that the function is compile-time evaluated.

Nicol Bolas

unread,
Aug 7, 2018, 11:19:52 AM8/7/18
to ISO C++ Standard - Future Proposals
But that behavior is not "what I want".

Basically, there are 3 possibilities in C++: required compile-time, implementation-dependent compile-time, and actual runtime. This tool only allows us to distinguish between the first and the rest. What users need is to distinguish the last from the rest. After all, that's why you guard these blocks; they're for stuff that can't happen at compile-time/runtime, regardless of how the compiler decides when to run which.

When exactly would I care about the difference between required compile-time and implementation-dependent compile-time? If I'm doing logging, which can't be done at compile time, I don't care why the function is being run at compile-time; I just need to not do logging in those cases.

Richard Smith

unread,
Aug 7, 2018, 7:24:19 PM8/7/18
to std-pr...@isocpp.org
On Tue, 7 Aug 2018 at 08:19, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, August 7, 2018 at 1:41:17 AM UTC-4, Ville Voutilainen wrote:
On 7 August 2018 at 03:51, Nicol Bolas <jmck...@gmail.com> wrote:
> In fact, looking over P0595, it's actually negatively useful as a feature.
> It is only allowed to evaluate to "true" in cases where the expression is
> required to be a constant expression; it must evaluate to "false" in other
> cases, even if the compiler wants to run it at compile time. So you cannot
> use it for cases where there is a divergence between efficient the
> compile-time and runtime solutions.
>
> So what good is the feature at all?

It gives you a portable result, as opposed to a result that depends on
whether your implementation
folds or not. Just use a normal if instead of if constexpr and it'll
do what you want.

But that behavior is not "what I want".

Basically, there are 3 possibilities in C++: required compile-time, implementation-dependent compile-time, and actual runtime.

If I understand correctly, you're distinguishing between:

1) required compile-time
2a) not required compile-time, does not happen at runtime on the physical machine (because it was simplified or removed during compilation)
2b) not required compile-time, actually happens at runtime on the physical machine

Under the as-if rule, the difference between 2a and 2b is unobservable.

But there are other problems with this taxonomy. The big one is that usually any given expression or statement is somewhere between 2a and 2b, with some parts simplified or removed during compilation and other parts actually executed. And I think you're also assuming that the compiler's choice to move cases between 2a and 2b necessarily has something to do with constant evaluation, which isn't really right. Constant evaluation happens to just be one of the tools that some compilers choose to use to try to move cases from 2b towards 2a -- but that's merely an implementation detail.
 
This tool only allows us to distinguish between the first and the rest. What users need is to distinguish the last from the rest. After all, that's why you guard these blocks; they're for stuff that can't happen at compile-time/runtime, regardless of how the compiler decides when to run which.

When exactly would I care about the difference between required compile-time and implementation-dependent compile-time? If I'm doing logging, which can't be done at compile time, I don't care why the function is being run at compile-time; I just need to not do logging in those cases.

What you're asking for is underspecified to the point that it's not reasonable to evaluate its merits. Moving code from 2b to 2a isn't some grandiose monolithic action; it's something that's potentially done piecewise. For example, if you have some code that says

if (!nicol_bolas_is_constant_evaluated())
  my_logger << "something something\n";

then is it your intent that the compiler is permitted to delete that if-statement and still execute the surrounding code at runtime? If not, why not? Does it matter if the logic inside the compiler that does this has something to do with its constant expression evaluator or is in a different piece of the compiler?

Arthur O'Dwyer

unread,
Aug 7, 2018, 8:09:26 PM8/7/18
to ISO C++ Standard - Future Proposals
Sure, but we're used to talking about the constexpr-evaluation action on a function-by-function basis. Otherwise, Richard, wouldn't all your arguments apply just as well to explain why marking individual functions as "constexpr" was a bad idea?

 
For example, if you have some code that says

if (!nicol_bolas_is_constant_evaluated())
  my_logger << "something something\n";

then is it your intent that the compiler is permitted to delete that if-statement and still execute the surrounding code at runtime? If not, why not? Does it matter if the logic inside the compiler that does this has something to do with its constant expression evaluator or is in a different piece of the compiler?

I would interpret that snippet as meaning, "If this function is being inlined 'in a constexpr context', then don't do logging."  Now, the reason we don't want to do logging is because it wouldn't compile as 'constexpr', because it calls non-constexpr functions to do the printing. So just a plain old `if` isn't good enough; we actually need `if constexpr` to make sure the compiler doesn't try to generate code for it — so that the functionality that's left in our function is all constexpr-friendly.

Or, in general, another reason we might want "if constexpr context" is so that we can use different algorithms at runtime versus compile-time — e.g., for performance reasons.  See the example of "sqrt" worked out in https://quuxplusone.github.io/blog/2018/06/12/perennial-impossibilities/#detect-the-constexprness-of-the-current-context  If the compiler had to instantiate both branches, it seems like that would tend to defeat the purpose.

Now, for a sufficiently smart compiler, there's a difference between instantiating both branches (which increases compile time) and codegenning both branches (which increases code size). Maybe that's what saves us here: all compilers will be sufficiently smart and we'll get the speed benefit on `sqrt` for only a mild compile-time hit. I'd like to see an implementation, though.

(And yes, "constexpr context" is basically impossible to define. However, I think we're all worried about the WG21 equivalent of the Politician's Fallacy: "We must do something that is implementable — this is implementable — therefore we must do this.")

(Sufficiently smart tooling could also solve the `if constexpr` problem, by emitting a warning diagnostic any time `is_constexpr_evaluated()` was used in a constant context. But tooling has not kept up with the pace of C++'s syntactic pitfalls in general, so I'm reluctant to rely on it here.)

my $.02,
–Arthur

Barry Revzin

unread,
Aug 7, 2018, 9:13:01 PM8/7/18
to ISO C++ Standard - Future Proposals
Or, in general, another reason we might want "if constexpr context" is so that we can use different algorithms at runtime versus compile-time — e.g., for performance reasons.  See the example of "sqrt" worked out in https://quuxplusone.github.io/blog/2018/06/12/perennial-impossibilities/#detect-the-constexprness-of-the-current-context  If the compiler had to instantiate both branches, it seems like that would tend to defeat the purpose. 

Now, for a sufficiently smart compiler, there's a difference between instantiating both branches (which increases compile time) and codegenning both branches (which increases code size). Maybe that's what saves us here: all compilers will be sufficiently smart and we'll get the speed benefit on `sqrt` for only a mild compile-time hit. I'd like to see an implementation, though.

(And yes, "constexpr context" is basically impossible to define. However, I think we're all worried about the WG21 equivalent of the Politician's Fallacy: "We must do something that is implementable — this is implementable — therefore we must do this.")

There's a definition for "required to be constant-evaluated" in p0595r1. Are there any cases that you think that doesn't cover? 

The example in the blog, sqrt, isn't a template, so there isn't any instantiating going on at all. Even if a hypothetical "if constexpr (std::is_constant_evaluated())" actually behaved like "if (std::is_constant_evaluated())" and not like "if constexpr (true)", the only difference would be have to do with whether or not some of the names are considered odr-used. Other than that, both branches would have to be parsed and evaluated anyway.

It really does not make sense to use "if constexpr" here - we're not in a template. "if constexpr" isn't about like... branch performance, it's about conditional instantiation. Compilers are pretty good about turning if (false) { ... } into no code.

Dawid Pilarski

unread,
Aug 8, 2018, 1:45:08 AM8/8/18
to std-pr...@isocpp.org
@Nicol Bolas
On Tuesday, August 7, 2018 at 1:53:14 AM UTC-4, Dawid Pilarski wrote:
@Nicolas Lesser

I will try to share with you my motivation.
there can be some numerical computation done in the constexpr function. For me it's important, that in some usages it will be compile time evaluated, otherwise I cannot fulfill my agreement with the user regarding performance.
So now I want to call it 
//context
my_contexpr_function_here(/*args*/);
//context
issues:
1. There is no way of checking whether the function really got executed in compile time,
2. There is no way if informing the reader (instead of the comment) to let him know that he must not break the compile-time evaluation of constexpr function.
`constexpr!` handles both of those. You cannot call a `constexpr!` function with non-constexpr parameters, and the return value is always `constexpr`.
So in my case after writing things down, to be sure, that function is compile-time evaluated I needed to check the generated code.
`constexpr!` ensures that the function is compile-time evaluated.

That is true indeed and it's not what I worry about. Let's assume, that `my_constexpr_function_here(/*args*/)` is actually constexpr!.

//context
my_contexpr_function_here(/*args*/); //must be compile time evaluated to meet performance expectations
//context

//----- few files of code later ---/
//runtime-context
my_contexpr_function_here(/*runtime-args*/); //damn... is there any non constexpr! version of this function? //note no such issue with regular constxpr functions
//runtime-context

that scenario is fine then, but if user won't provide non constexpr! version of calculation method, then user is stuck.
This is why I thought, that maybe it's better to provide some calling level syntax to force evaluation of function in compile-time.

To sum up, what I want to say:

If there is a constexpr! function designed for non-type, but numerical computations, then it can still be useful to call it in non-constexpr contexts and developer (implementing the constexpr function)
might simply skip this opportunity, because it's easy to skip such things if you forget about how users can use your library.



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Aug 8, 2018, 2:34:30 PM8/8/18
to ISO C++ Standard - Future Proposals
I guess that's my main issue with that `is_constexpr_context` proposal: is it solving a useful problem? What exactly is the code that a user needs to protect from required compile-time evaluation that would not also need protection from implementation-determined compile-time evaluation?

All of the examples given in the proposal are so abstract that they're meaningless. So what are the real-world examples of code where such determination is genuinely useful?

Because we have cases where distinguishing between compile-time evaluation of any kind and runtime evaluation is useful. Things like using compiler intrinsics or inline assembly to do low-level things like sqrt and so forth. We want to be able to make such functions `constexpr`-callable, but the body of the `constexpr` version cannot use compiler intrinsics (presumably) or inline assembly. So you would condition which function gets called based on whether it is being evaluated at compile time.

It should also be noted that the `constexpr!` proposal doesn't talk about overloading. That is, there's nothing about what happens if you have two function declarations that differ only on the basis of `constexpr!`. So it doesn't seem to allow you to have two function bodies with the same name, such that the compiler will call one at compile-time and the other at runtime, based on how it gets used.

So even P1073 doesn't really give us this power.

Barry Revzin

unread,
Aug 8, 2018, 6:31:17 PM8/8/18
to ISO C++ Standard - Future Proposals


On Wednesday, August 8, 2018 at 1:34:30 PM UTC-5, Nicol Bolas wrote:
I guess that's my main issue with that `is_constexpr_context` proposal: is it solving a useful problem? What exactly is the code that a user needs to protect from required compile-time evaluation that would not also need protection from implementation-determined compile-time evaluation?

All of the examples given in the proposal are so abstract that they're meaningless. So what are the real-world examples of code where such determination is genuinely useful?

Because we have cases where distinguishing between compile-time evaluation of any kind and runtime evaluation is useful. Things like using compiler intrinsics or inline assembly to do low-level things like sqrt and so forth. We want to be able to make such functions `constexpr`-callable, but the body of the `constexpr` version cannot use compiler intrinsics (presumably) or inline assembly. So you would condition which function gets called based on whether it is being evaluated at compile time.

It seems like you answered your question from your first paragraph in this paragraph? That's exactly what is_constant_evaluated() is for - to do one operation or the other (not which function gets called, but just what gets run in the body). It's not just about compiler intrinsics and stuff either, it's also to do something like... disable SSO when creating a std::string so that we can have constexpr std::strings. They don't have to be different functions, just in the body of the same function:

string::string(char const* p, size_t len) {
   
if (is_constant_evaluated() || len > whatever) {
        _buffer
= new char[len + 1];
       
// ...
   
} else {
       
// SSO stuff
   
}
}

 

It should also be noted that the `constexpr!` proposal doesn't talk about overloading. That is, there's nothing about what happens if you have two function declarations that differ only on the basis of `constexpr!`. So it doesn't seem to allow you to have two function bodies with the same name, such that the compiler will call one at compile-time and the other at runtime, based on how it gets used.

So even P1073 doesn't really give us this power.

It just doesn't work that way.  

Nicol Bolas

unread,
Aug 8, 2018, 8:59:00 PM8/8/18
to ISO C++ Standard - Future Proposals
On Wednesday, August 8, 2018 at 6:31:17 PM UTC-4, Barry Revzin wrote:
On Wednesday, August 8, 2018 at 1:34:30 PM UTC-5, Nicol Bolas wrote:
I guess that's my main issue with that `is_constexpr_context` proposal: is it solving a useful problem? What exactly is the code that a user needs to protect from required compile-time evaluation that would not also need protection from implementation-determined compile-time evaluation?

All of the examples given in the proposal are so abstract that they're meaningless. So what are the real-world examples of code where such determination is genuinely useful?

Because we have cases where distinguishing between compile-time evaluation of any kind and runtime evaluation is useful. Things like using compiler intrinsics or inline assembly to do low-level things like sqrt and so forth. We want to be able to make such functions `constexpr`-callable, but the body of the `constexpr` version cannot use compiler intrinsics (presumably) or inline assembly. So you would condition which function gets called based on whether it is being evaluated at compile time.

It seems like you answered your question from your first paragraph in this paragraph? That's exactly what is_constant_evaluated() is for - to do one operation or the other (not which function gets called, but just what gets run in the body). It's not just about compiler intrinsics and stuff either, it's also to do something like... disable SSO when creating a std::string so that we can have constexpr std::strings. They don't have to be different functions, just in the body of the same function:

string::string(char const* p, size_t len) {
   
if (is_constant_evaluated() || len > whatever) {
        _buffer
= new char[len + 1];
       
// ...
   
} else {
       
// SSO stuff
   
}
}


And what happens if the compiler wants to execute that at compile time in a non-constant expression context? It can't, because "SSO stuff" can't be executed at compile time. Whereas if `is_constant_evaluated` had returned true during non-constexpr compile time execution, it could have worked.

It should also be noted that the `constexpr!` proposal doesn't talk about overloading. That is, there's nothing about what happens if you have two function declarations that differ only on the basis of `constexpr!`. So it doesn't seem to allow you to have two function bodies with the same name, such that the compiler will call one at compile-time and the other at runtime, based on how it gets used.

So even P1073 doesn't really give us this power.

It just doesn't work that way.

It doesn't work which way?

Edward Catmur

unread,
Aug 9, 2018, 12:12:30 PM8/9/18
to ISO C++ Standard - Future Proposals
Huh? It's absolutely fine for a `constexpr` function to contain constexpr-unfriendly code, as long as that code isn't *executed* when the function is evaluated in a constexpr context.

#include <cstdlib>
constexpr int fib(int n) {
 
if (n < 0) std::abort();
 
if (n <= 1) return n;
 
return fib(n - 1) + fib(n - 2);
}
static_assert(fib(7) == 13);
int f() { return fib(8); }  // optimized to "return 21"
int g(int i) { return fib(i); }

A few constructs aren't allowed, such as asm, but that's easily worked round with an IIFE.

Ville Voutilainen

unread,
Aug 9, 2018, 12:31:23 PM8/9/18
to ISO C++ Standard - Future Proposals
The compiler can execute and constant-fold whatever it likes under the
as-if rule. But
is_constant_evaluated and some sort of "is compiler trying to do
constant folding even though
it doesn't have to" are two very different
things. Just because a compiler is trying to do constant folding
doesn't mean it should pick
the code path chosen by the condition on is_constant_evaluated().
Furthermore, a compiler
doesn't have to fold anything if it's not compiling a constant
expression context. The
is_constant_evaluated() branch can have code in it that doesn't do
anything sane at
runtime (example of such code is invoking a compiler intrinsic that
allocates "compiler memory")
and vice versa, the run-time branch can have code in it that doesn't
do anything sane at
compile-time. The answer must be portable for such code to work, and
must not rely on
implementation QoI, which is what constant folding optimizations are.

If you want "are you trying to fold, mr. compiler?", feel free to
propose it separately.

Edward Catmur

unread,
Aug 9, 2018, 12:37:55 PM8/9/18
to ISO C++ Standard - Future Proposals


On Thursday, 9 August 2018 01:59:00 UTC+1, Nicol Bolas wrote:
On Wednesday, August 8, 2018 at 6:31:17 PM UTC-4, Barry Revzin wrote:
On Wednesday, August 8, 2018 at 1:34:30 PM UTC-5, Nicol Bolas wrote:
I guess that's my main issue with that `is_constexpr_context` proposal: is it solving a useful problem? What exactly is the code that a user needs to protect from required compile-time evaluation that would not also need protection from implementation-determined compile-time evaluation?

All of the examples given in the proposal are so abstract that they're meaningless. So what are the real-world examples of code where such determination is genuinely useful?

Because we have cases where distinguishing between compile-time evaluation of any kind and runtime evaluation is useful. Things like using compiler intrinsics or inline assembly to do low-level things like sqrt and so forth. We want to be able to make such functions `constexpr`-callable, but the body of the `constexpr` version cannot use compiler intrinsics (presumably) or inline assembly. So you would condition which function gets called based on whether it is being evaluated at compile time.

It seems like you answered your question from your first paragraph in this paragraph? That's exactly what is_constant_evaluated() is for - to do one operation or the other (not which function gets called, but just what gets run in the body). It's not just about compiler intrinsics and stuff either, it's also to do something like... disable SSO when creating a std::string so that we can have constexpr std::strings. They don't have to be different functions, just in the body of the same function:

string::string(char const* p, size_t len) {
   
if (is_constant_evaluated() || len > whatever) {
        _buffer
= new char[len + 1];
       
// ...
   
} else {
       
// SSO stuff
   
}
}


And what happens if the compiler wants to execute that at compile time in a non-constant expression context? It can't, because "SSO stuff" can't be executed at compile time. Whereas if `is_constant_evaluated` had returned true during non-constexpr compile time execution, it could have worked.

Well, that's not quite true. When the compiler is lifting code from runtime to compile time, it's perfectly at liberty to execute UB and USB at compile time that would be invalid in an actual constexpr context.

#include <cstring>
int n() {
 
float f = 5.0;
 
int i;
 std
::memcpy(&i, &f, sizeof(i));
 
return i; // optimizes to "return 1084227584"
}

yakito...@gmail.com

unread,
Aug 11, 2018, 7:21:12 PM8/11/18
to ISO C++ Standard - Future Proposals
constexpr! is funcy object.
i think it is like high level preprocesser function without IO.
some peason hate preprocesser.
delete one mean from preprocesser.
it never has no meaning.

and...
it can hide the someone's secret.
maybe campany is needed??

i not have unhappiness for normal constexpr.
Reply all
Reply to author
Forward
0 new messages