#virtual constexpr

1 messages · Page 1 of 1 (latest)

wintry agate
#

why would the compiler error on a->foo saying it's not usable in a constant expression?```cpp
struct A
{
constexpr virtual int foo()
{
return 42;
}
virtual ~A() = default;
};

struct B : public A
{
constexpr int foo() override
{
return 10;
}
};

constexpr int bar()
{
A* a = new B{};
constexpr int val = a->foo(); // 'a' not usable
delete a;
return 5;
}

int main()
{
constexpr int b = bar();
}````

onyx brambleBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question run !howto ask.

gritty saddle
#

We’ve had a long ass discussion about another identical thing

final inlet
#

Because the standard doesn't allow for constexpr calls to a virtual method

wintry agate
#

wut

final inlet
#

I'm assuming anyway

wintry agate
#

it's there since c++17 or 20

final inlet
#

Because you can call virtual methods non virtually

gritty saddle
#

‘a’ here requires a lvalue to rvalue conversion

#

Its the same story as the other day we discussed this most likely

wintry agate
#

i don't recall it. i never brought up a discussion on virtual constexpr before anyway

#

i don't get why the conversion

#

why would this work then

struct Base {
    constexpr virtual int get() { return 1; }
    virtual ~Base() = default;
};

struct Child : Base {
    constexpr int get() override { return 2; }
};

constexpr int foo(bool b) {
    Base* ptr = b ? new Base() : new Child();
    auto res = ptr->get(); // this call is not possible prior to C++20
    delete ptr;

    return res;
}
gritty saddle
#

its not constexpr

wintry agate
#

just cause they are using auto?

final inlet
#

Quick question, how are name collisions handled with multiple inheritance?

wintry agate
#

wrong thread bro

gritty saddle
#

Because its not constexpr

#

Mark it constexpr and it stops working

wintry agate
gritty saddle
#

lvalue to rvalue conversion most likely

final inlet
# wintry agate wrong thread bro

Not necessarily
How to handle the edge cases multiple inheritance brings could affacet the standards comitees decision about this topic

wintry agate
#

i don't get this. mind elaborating?

gritty saddle
#

‘a’ is not usable in constant expressions

#

when you read a variables value (eg a->) it undergoes lvalue to rvalue conversion

#

the rule says that this works only if a’s lifetime started during the evaluation of the constexpr expression (false) or if a is usable in constant expressions, which is also false

wintry agate
#

but as lifetime did start during the evaluation of a constant expression no?

gritty saddle
#

no

#

A* a = … // starts here

final inlet
#

So if constexper A* a ... then it would have worked?

gritty saddle
#

no

#

because of the new

final inlet
#

Makes sense

gritty saddle
#

and also b’s lifetime didnt start there

#

its really tricky

wintry agate
gritty saddle
#

doesnt matter

final inlet
#

How about?
constexpr B b;
constexpt A* a = &b;

gritty saddle
#

you need to check the current constexpr

wintry agate
#

i don't follow what you mean by a's lifetime not starting within

gritty saddle
#

And 483853948 rules lol

final inlet
#

Understandable

gritty saddle
#

You can initialize a constexpr variable with a function call even if not everything is a constant expression inside the function

wintry agate
#

sure but how does that mean. 'a's lifetime doesn't start within the constant expression?

gritty saddle
#

constexpr int val = …

#

this is the constant expression

#

and here a already exists

wintry agate
#

sure so A* a = new B{}; isn't cause it doesn't use constexpr?

gritty saddle
#

Isn’t what?

#

A constant expression?

#

It is not, thats correct

wintry agate
#

and you can't do constexpr A* a = new B{};?

gritty saddle
#

You could depending on B

#

Well no

#

With new not

#

New requires a delete in the same expression

#

Otherwise it cant be a constant expression

#

melaks example in the help channel proved that

wintry agate
#

if dynamic allocation can now be in compiile time why can't it be constexpr

gritty saddle
wintry agate
#

same expression?

gritty saddle
#

Yes

wintry agate
#

there's already delete in my example

gritty saddle
#

See melaks example

gritty saddle
wintry agate
#

i have. he's deleting in the dtor

gritty saddle
#

The expression ends after ;

wintry agate
#

whereas i am manually deleting it

gritty saddle
#

So you didnt delete it in that expression

#

But melak did

#

Because return … is the whole expression

wintry agate
#

how can u new and delete in the same expression lol

gritty saddle
#

return foo->func();

#

and in func() you both allocate and delete

#

Its all within the expression’s evaluation, so it works

final inlet
#

So the fact that the pointer is deleted allows for the function as a whole to remain constexpr, but the individual expressions using that pointer aren't constexpr?

gritty saddle
#

Yep

wintry agate
#

B2 b; allocates

gritty saddle
#

I see

#

But still

wintry agate
#

?

gritty saddle
#

They dont use constexpr there

#

Otherwise what I said should work

wintry agate
#

uh

gritty saddle
#

They use constexpr in main

wintry agate
#

i asked about why not using constexpr for new

gritty saddle
#

I told you?

#

You can but not the way you’re doing it

wintry agate
#

you said you gotta allocate and deallocate in the same expression which melak isn't doing?

gritty saddle
#

Wait, maybe cppref got an example

gritty saddle
#

If you didnt your program would compile, too

wintry agate
#

i know but you try using constexpr it won't compile

gritty saddle
#

Exactly?

#

Thats the whole point

wintry agate
#

i don't get the point?

#

why can't you use it

#

if it can run at compile time

gritty saddle
#

constexpr int naiveSum(unsigned int n) {
    auto p = new int[n];
    std::iota(p, p+n, 1);
    auto tmp = std::accumulate(p, p+n, 0);
    delete[] p;
    return tmp;
}

constexpr int smartSum(unsigned int n) {
    return (n*(1+n))/2;
}

int main() {
    static_assert(naiveSum(10) == smartSum(10));        
    static_assert(naiveSum(11) == smartSum(11));
    return 0;
}
#

See this example

#

the static assert is a compile time construct so here the call to naiveSum(10) is all compile time

#

naiveSum(10) is THE expression

wintry agate
#

true, but again why not use constexpr for new

gritty saddle
#

because you cant just do that

wintry agate
#

why not

gritty saddle
#

Are you reading what Im typing?

#

For new to be used in a constant expression you need delete in that expression as well

wintry agate
#

not sure how that relates tho. my example is similar where bar runs in compiile time (similar to melak's as well)

gritty saddle
#

You are confusing what the constant expression is

#

Here, the function call is a constant expression

wintry agate
gritty saddle
#

That doesnt mean you need everything to be a constant expression is such a function

gritty saddle
#

And in that way, it works

wintry agate
#

but that's not the question

gritty saddle
#

But see what you’re missing: the constant expression is the function call

wintry agate
#

my question was about using constexpr for new

final inlet
wintry agate
final inlet
#

One moment

gritty saddle
#

So if you mean constexpr int x = new int(5); it wont work because no delete during the expressions evaluation

wintry agate
#

yeah either we are using diff terms cause it's getting confusing. you said constant expression is the function call didn't you?

gritty saddle
#

Yes

wintry agate
#

if bar is run in compile time, doesn't it mean new and delete are in the same constant expression?```cpp
constexpr int bar()
{
constexpr A* a = new B{};
auto val = a->foo();
delete a;
return val;
}

gritty saddle
#

No

#

Absolutely not

wintry agate
#

then what did you mean by function call being a constant expression

gritty saddle
#

bar() might be usable in a constant expression (use its result to initialize a constexpr variable)

wintry agate
#

if you remove the delete call from here, it would complain

gritty saddle
#

But the contents of bar() dont need to be constant expressions themselves

#

eg. All constexpr

wintry agate
#

they don't need to be but it depends. if you wanna define a variable and pass it to another constexpr it better be constexpr

final inlet
wintry agate
final inlet
#

The expressio ends at the semicolon

wintry agate
final inlet
#

There is a delete in the function, but that ONLY allows the function AS A WHOLE to be constexpr

final inlet
gritty saddle
#

;compile -std=c++20

constexpr int foo(){int x=5; return x;}
int main(){
  constexpr int y = foo();
}```
thin blazeBOT
#
Compilation successful

No output.

wintry agate
wintry agate
#

yea. x doesn't have to constexpr here

gritty saddle
#

The same way your variables dont need to be constexpr in the function

tender grove
wintry agate
#

but I have been asking why does new can't be constexpr? and not it doesn't have to be

gritty saddle
#

I told you

final inlet
gritty saddle
#

Like at least five times

tender grove
#

you can use new during constant evaluation. but only of the thing you new is also deleted again before the end of the expression it's created in.

wintry agate
#

it's only deleted once

tender grove
#

it must be deleted inside the same constant expression it's created in

wintry agate
#

yes

tender grove
#
    A* a = new B{};
    constexpr int val = a->foo();
    delete a;
```this doesn't fulfill that
wintry agate
#

yes so in the link you just shared, bar is a constant expression within which a is allcoated and deleted?

tender grove
#

since val is constexpr, its initializing expression must be a constant expression

wintry agate
#

right

tender grove
#

but the thing a you use in that expression is created outside the expression

wintry agate
#

so basically foo must be constexpr

tender grove
#

remove the constexpr there and all is good

gritty saddle
#

You have a misunderstanding of something I can’t see really

#

I don’t know what it is

wintry agate
#

foo?

tender grove
#

a->foo()

#

is the expression

#

and it uses smth that was created outside the expression

#

hence it's not a constant expression

wintry agate
#

sorry. if a->foo() itself is n expression, it uses a which was created outside of it hence it's not a constant expression?

tender grove
#

yes

#

a was not created during evaluation of a->foo()

#

so a->foo() is not a constant expression

gritty saddle
#

My teaching skills suck lets leave it at that

wintry agate
#

this would've been a constant expression since there's nothing that's used is outside of the expression?constexpr int a = foo();

and the expression being foo()?

gritty saddle
final inlet
tender grove
#

the key thing to understand is that bar() could be called at runtime.

#

then a would actually be created with new at runtime

#

and then there's absolutely no way that a->foo() is gonna be a constant expression

#

just remove the constexpr on val and it'll just work

#

because then you just new and delete inside the constexpr function

#

and if the constexpr function is called from a constant expression

#

then a will be newed and deleted inside that constant expression

#

and all is well

#

the problem is just with putting constexpr on val inside bar

gritty saddle
#

Not only is it a tricky topic its also amazingly tricky to explain

tender grove
#

yeah ^^

#

constexpr is very tricky and generally very poorly understood ^^

wintry agate
tender grove
#

it uses a which is created outside of a->foo()

#

thus a->foo() itself is not a constant expression

#

and so it cannot be used as the initializer of a constexpr variable

wintry agate
#

despite foo being a constexpr?

tender grove
#

that's utterly irrelevant

#

how does foo being constexpr change anything?

#

your expression isn't foo()

#

your expression is a->foo()

#

the whole expression must be a constant expression

#

and you cannot call foo() without an object since it's a non-static member function

wintry agate
#

sure but you can't initialize a constexpr variable with a non-constexpr function

tender grove
#

yes

#

ofc not

wintry agate
tender grove
#

yeah that seems to be a gcc bug

#

which is why the version i linked uses msvc 😛

wintry agate
gritty saddle
#

Inside the function for example

#

The function call itself is an expression

#

So you both new and delete in said expression

#

Obviously int z = new (…) delete(…); makes no sense

#

Well maybe with the comma operator 😮 well no nevermind lmao

tender grove
#

then you new and delete during evaluation of that expression

#

which is why your initial example works if you just remove the constexpr from val

#

because then there's only one constant expression you're evaluating: the one that calls bar()

#

and the new and delete both happen inside bar()

#

so all is well

#

btw, might wanna report that gcc bug

wintry agate
#

I think it makes sense. Might need a break for a bit now. Thanks. Will revisit it later today

onyx brambleBOT
#

@wintry agate Has your question been resolved? If so, run !solved :)

wintry agate
#

msvc v19.latest doesn't work here but worked for dot :/
https://godbolt.org/z/h3jqWTbcr

as a follow up, if you can use new in a constant expression, why not vector?

gritty saddle
#

"cl : Command line warning D9002 : ignoring unknown option '-std=c++20'"

#

You need this flag for MSVC: /std:c++20

#

And everything works fine

#

Not sure what the issue with GCC is. It works with MSVC and Clang

tender grove
#

as I said, pretty sure that's a bug in gcc

#

I guess nobody ever tested constexpr deletion of a derived class though a virtual destructor from a base class pointer in gcc 😋

#

this isn't exactly a feature anyone actually really uses yet ^^

gritty saddle
#

And idk why std::vector doesn't work

#

It should be usable in constexpr

#

at least constexpr std::vector myVec {15, -5, 0, 5, 10}; should be valid in C++20, yet no compiler seems to support it

gritty saddle
#

Don’t they test them before release

#

Like any new rule there is

#

Not sure I’d want to spend my time doing that either but uhh 😂

gritty saddle
#

Ah yeah, needs an allocator’s deallocate in the same evaluation

tender grove
#

yup

gritty saddle
#

Ive seen the example on Modernes and assumed it to be correct

#

Silly me

tender grove
#

^^

gritty saddle
#

;compile -std=c++20


#include <vector>

constexpr bool testVector(int n) {
    std::vector<int> vec(n, 1);

    int sum = 0;

    for (auto& elem : vec)
        sum += elem;

    return n == sum;
}

int main() {
    static_assert(testVector(10));
}

thin blazeBOT
#
Compilation successful

No output.

gritty saddle
#

Nais

#

Maybe later standards will release non transient allocations

#

Would be crazy

#

There are even some papers

wintry agate
#

kewl so vector does work but only in clang for me.
but when you use constexpr vector, you won't be able to push back since that's runtime yes?

gritty saddle
#

;compile -std=c++20


#include <vector>

constexpr bool testVector(int n) {
    std::vector<int> vec(n, 1);

    int sum = 0;

    for (auto& elem : vec)
        sum += elem;
    vec.push_back(5);
    return n == sum;
}

int main() {
    static_assert(testVector(10));
}

thin blazeBOT
#
Compilation successful

No output.

gritty saddle
#

@wintry agate seems fine

#

Once again, your confusion seems to be dictated from the fact that you think that initializing a constexpr expression with eg a function call testVector(10) requires that everything inside testVector(10) is a constant expression, which is simply untrue

tender grove
#

yup

wintry agate
#

again I didn't mean that

gritty saddle
#

That's what I think

wintry agate
#

core expression rules only apply to constexpr

gritty saddle
#

Seeing what you're asking

wintry agate
#

if vector isn't constexpr, core constant rules wont apply

gritty saddle
#

Ah, then yes, clearly

wintry agate
#

my question was really about using push_back when vector is constexpr

gritty saddle
#

constexpr also implies const

#

and yeah pushing back to a constexpr vector could lead to memory allocation, which is again an issue in that context

wintry agate
#

yea so what good does constexpr do to vector even then

#

might as well use array

tender grove
#

using an array requires you know the size beforehand

wintry agate
#

but you cant push to an existing vector in a constant context so how does that help

tender grove
#

you can still use a vector in your computation of the result of your constexpr function

wintry agate
#

what good for tho?

tender grove
#

whenever you're doing smth and don't know beforehand how much of smth you'll need?

#

you might as well be asking what's the use of vectors when arrays exist 🤷‍♂️

wintry agate
#

but you can't populate it after its defined?

tender grove
#

wdym

#

look at this code #1092566026262151380 message

#

you could be pushing back in that loop and whatnot

#

the only constraint is that the vector itself does not escape the constant expression

wintry agate
#

I am referring to constexpr vector

tender grove
#

you cannot have a constexpr vector

#

you can create a vector during constant evaluation

#

but a vector object itself cannot be constexpr

wintry agate
tender grove
#

well ok

#

you can potentially have an empty constexpr vector ^^

wintry agate
#

what good for?

tender grove
#

probably not much?

wintry agate
#

not anything I suppose

#

why was this introduced

#

that was my curiosity

tender grove
#

that's like asking why you can have a function that doesn't do anything

wintry agate
#

lol

tender grove
#

they didn't specifically introduce the ability to have empty constexpr vectors.

wintry agate
#

it's about them introducing something particularly for +20 I reckon

tender grove
#

they introduced the ability to to a limited form of dynamic object creation during constant evaluation

wintry agate
#

when you have constexpr std::array specially

#

what's the need to have constexpr vector

gritty saddle
#

If they introduce "non transient" memory management, it could potentially bring more utilities in this context

#

but that's an if

tender grove
#

the fact that you can have an empty constexpr vector is a consequence of that. not the purpose.

tender grove
wintry agate
#

fair

#

i guess

tender grove
#

to outlaw this, you need to now find a way to do that without also breaking all the stuff that you do actually want

#

also, who knows, maybe someone comes up with some clever use for it eventually

wintry agate
#

if vector can be used with constexpr then why can't new be

tender grove
#

as long as it's not ill-defined or smth, there's generally no need to forbid smth

gritty saddle
#

because the default constructor doesn't allocate anything -- all the rules are respected

#

I think they meant something like constexpr int x = new /.../

wintry agate
#

constexpr A* a = new B{};

tender grove
#

well that can't work ofc

wintry agate
#

but why would constexpr work with vector

tender grove
#

because creating an empty vector doesn't allocate anything?

wintry agate
#

vector uses new under the hood no?

tender grove
#

without having looked this up, my guess would be that it's actually implementation-defined whether creating an empty vector allocates anything or not

#

so i guess there's no actual guarantee that this will even work. it just happens to on most implementations.

#

(again, just a hunch, i'd have to look this up to confirm)

gritty saddle
#

Yeah nowhere is it written on cppreference

#

so I guess in this specific implementation it doesn't allocate, probably

tender grove
#

creating an empty constexpr vector

wintry agate
#

if it doesn't allocate i don't see a problem but if it does then i don't see why would constexpr not work with new

tender grove
#

i think chances are that an implementation would technically be allowed to allocate some initial buffer in the vector default ctor.

tender grove
#

that's literally all there is to it

#

everything we've discussed here is just a simple consequence of that rule.

wintry agate
tender grove
#

it does

#
constexpr A* a = new B{};
#

you don't delete within the same expression

#

so it's not a constant expression

#

simple as that

gritty saddle
#

If you allocate at compile time, how does the compiler know that you'll deallocate at run time?

#

I think that's the rationale

tender grove
#

it's more like: where would the compiler even put it? it has to assume that you're going to delete it at runtime.

wintry agate
#

ah. so lemme get this straight.

foo is a constant expression itself, and everything happens within it is in a constant context. So if you new, you gotta delete within the function.

however if you have constexpr <something>, you create a new constant expression and the rules only to it only

constexpr int foo() 
{
};

constexpr int b = foo();````
tender grove
#

constexpr <something> declares a <something> that's constexpr

#

what that means depends on <something>

#

but

constexpr A* a = new B{};
#

here, your <something> is a variable a of type A*

#

a constexpr variable requires a constant expression as its initializer

#

so new B{} would have to be a constant expression for this to work

#

but it isn't

#

so it doesn't

wintry agate
#

what could make a constant expression for new to work? primitives?

tender grove
#

for example:

constexpr int foo()
{
    int* blub = new int(42);
    int x = *blub;
    delete blub;
    return x;
}

constexpr int b = foo();
#

this works

#

because foo() is a constant expression

#

likewise, this would also work

constexpr int b = delete new int, 42;
tender grove
#

this wouldn't work for example:

constexpr int foo(bool b = false)
{
    if (b) return 42;
    return rand();
}

constexpr int b = foo();
#

because foo() is not a constant expression

#

despite foo being a constexpr function

#

foo(true) would be a constant expression though

#

the key thing to understand is that the only way to find out whether an expression is a constant expression is to try to evaluate it and see whether it can be evaluated within the rules of constant expression evaluation.

wintry agate
#

but arent you enforcing foo to be constantly evaluated by calling with constexpr?

#

we can force a constexpr function that is eligible to be evaluated at compile-time to actually evaluate at compile-time by ensuring the return value is used where a constant expression is required

tender grove
#

well yes

#

which is why this won't compile

wintry agate
#

exactly

#

i guess rand() is runtime

tender grove
#

yes

wintry agate
#

so it won't compile

#

so for vector, you can't initialize it and can only have an "empty" one with constexpr
for string, it's not allowed? i thought there were relaxations around it too
constexpr std::string s; // nope

tender grove
#

depends on the string and vector implementations i suppose

#

again, the rule is simple: creating the thing can't allocate smth that isn't also deallocated in the process.

gritty saddle
#

allocates/deletes, then assigns 42 to b

tender grove
#

yup

wintry agate
tender grove
#

lol

#

guess you just found another compiler bug 😛

gritty saddle
#

no?

#

the left expression is only used for side effects

tender grove
#

yeah but no compiler wants to parse this correctly it seems

#

not unless you wrap it in ()

gritty saddle
#

waat

#

= has higher precedente or what

tender grove
#

no, i'm pretty sure that's a bug in all 3 compilers ^^

gritty saddle
#

it does

tender grove
#

but i'll have to study the spec for that

tender grove
#

there's no precedence here

gritty saddle
#

how not?

tender grove
#

the = there is not an assignment expression

#

this is a declaration

#

the = just delimits the initializer

#

i'll look into it later today

gritty saddle
#

oh

tender grove
#

but i'm pretty sure this gotta be a bug

#

parsing C++ is tricky ^^

#

wouldn't be the first time that they all get it wrong

gritty saddle
#

Wellll

#

int x = (5, 6); works

#

int x = 5, 6; doesn't

#

I think it has something to do with
The comma in various comma-separated lists, such as function argument lists (f(a, b, c)) and initializer lists int a[] = {1, 2, 3}, is not the comma operator. If the comma operator needs to be used in such contexts, it has to be parenthesized: f(a, (n++, n + b), c) but indirectly

#

different context, but same rule? no idea

tender grove
#

ah yes, that's true

#

my bad

#

then all is well

onyx brambleBOT
#

This question is being automatically marked as stale.
If your question has been answered, run !solved.
If your question is not answered feel free to bump the post or re-ask.
Take a look at !howto ask for tips on improving your question.

umbral halo
#

maximally vexing parse (the entire language)

wintry agate
#

might have been discussed but outta curiosity, when would one even want to have constexpr for ret? to not rely on the caller (like here foo is guaranteed to run in compile time & so is increment)

constexpr int foo(int value)
{
    int ret = increment(value); // core constant rules don't apply
    return ret;
}
constexpr int v = foo(5);
tender grove
#

for the same reasons you would want to have constexpr outside: so that you can use it where a constant expression is required

wintry agate
#

fair, and you could never pass a function parameter (runtime) to a function that's running in compile time

wintry agate
#

if static variables are defined in runtime, how'd be possible to use them in a constexpr function?

final inlet
#

I imagine they can be defined at compile time if there are no runtime dependencies

wintry agate
#

constexpr functions can not have static or thread_local data. Neither can they have a try block nor a goto instruction. flooshed

tender grove
#

they can have static local variables in C++23

#

iirc

#

thread_local doesn't make much sense ofc

#

since there are no threads at compile time

gritty saddle
#

What’s the rationale of disallowing static locals

tender grove
#

This makes sense for the perspective of a static (or, worse, thread_local) variable, whose initializer could run arbitrary code. But for a static constexpr variable, which must, by definition, be constant-initialization, there’s no question about whether and when to run what initialization. It’s a constant.

gritty saddle
#

This is ill-formed. And it’s ill-formed for no good reason. We should make it valid.

#

Good kekw

tender grove
#

there you go

#

constexpr started out very very restrictive for many reasons, mostly because having even that limited amount of constexpr was already a tricky thing to get done. if they had waited until they had figured out all the constexpr, we'd likely still not have constexpr in the langauge like at all.

#

we've spent the last decade+ slowly but steadily abolishing all the unnecessary restrictions

#

so yeah, this is just one of those unnecessary restrictions being lifted 🤷‍♂️

wintry agate
#

why is that local static matters? shouldn't it be whether could just static variables be used?

#

constexpr static is compile time but static alone ... isn't no?

tender grove
#

wdym

wintry agate
#

static vars here are defined at compile time and initialized in runtime? I am wondering about its usage in a constant context

static int glob = 100;
constexpr int foo()
{
  static int local = 4;
  return 10;
}```
tender grove
#

no they're all initialized at compile time

wintry agate
#

does the standard guarantee it tho? i couldn't find any resource

#

also if it does indeed is initialized at compile time what good does constexpr static do

tender grove
#

constexpr has nothing to do with initialization at compile time

#

constexpr on a variable just means that the value is know at compile time

#

not that the object is created at compile time

#

C++20 has constinit

wintry agate
#

which value? that's used to initialize? so iin the above snippet 4 for local?

tender grove
#

example

void fun()
{
    constexpr int x = 42;
}
#

here, it is known at compile time that the value of x is 42

#

but x is not initialized at compile time

#

it couldn't possible be

#

it's a local variable

#

if an object is ever created, it would live on the stack

#

constexpr really just means "value known at compile time"

#

it says nothing about when smth is created or initialized

#

that's why consteval and constinit exist in the first place

wintry agate
tender grove
#

a constexpr variable must be initialized with a constant expression

wintry agate
#

so the object is created in runtime that's initialized with a compile time value?

tender grove
#

if you call the function at runtime, the object is created at runtime, yes

#

(ofc the object will almost certainly be optimized away in this particular case)

wintry agate
#

exactly so local variables can indeed be computed at compile time as long as they're evaluated with a constant expression which is what got me confused. you mentioned about object living on the stack has to be in runtime but i guess the bigger picture was missing

tender grove
#

their value is known at compile time

#

the object is not created at compile time

wintry agate
tender grove
#

yes

#

but then it's not created at compile time

#

the point is, it can only be created at runtime

wintry agate
#

when is it not created at compile time? are you making a general statement

tender grove
#

you cannot take the address of the object and use it in a constant expression

#

because it's not known where the object will exist

wintry agate
#

foo here runs in runtime?cpp constexpr int val = foo();

tender grove
#

yes

#

i mean, it might

wintry agate
#

so how does val get evaluated at compile tiem?

#

why it might and not guaranteed? the return value should dictate that no?

tender grove
#

whether val gets evaluated at compile time or not depends on how it is used

wintry agate
#

i don't see how val won't be evaluated at compile time here

tender grove
#

if you use val where a constant expression is required, it will be evaluated at compile time

wintry agate
#

ofcourse foo has to be constexpr

tender grove
#

if you use val at runtime or smth, it could be evaluated at runtime

wintry agate
#

it depends on how it's used

tender grove
#

a new object is created whenever the function is called

#

the object cannot be initialized at compile time

#

is what i meant

wintry agate
#

or initialized

tender grove
#

i'm saying val is created every time the function is invoked

#

and it will be initialized every time it is created

#

that can be at runtime, it can also be during evaluation at compile time ofc

wintry agate
#

but you said object can't be initialized at compile time and I am asking if foo is to run at compile time, initialization would indeed happen at compile time

tender grove
#

yes

#

at compile time, it happens at compile time

#

but there's not just one object val

wintry agate
tender grove
#

is the point

wintry agate
tender grove
#

val is a variable that denotes an object that's created every time the function is called

wintry agate
#

what's this object thing we are referring to? not val, not the return value, then what is it

tender grove
#

the object the variable val denotes

#

every function invocation has its own instances of local variables

#

and the initialization of these happens when the function is called

#

there's not just one object val

#

there's another val in every call of the function

wintry agate
#

yes. i understand. the answer still is the object creation and initialization is dependent on the usage and not that it can't be created at compile time

tender grove
#

yes, i tried to use simple words to make a point as opposed to being 100% correct

wintry agate
#

yea it got confusing

#

my understanding really was as long as constant expression exists, compile time evaluation takes place

tender grove
#

my point was that the object val is not simply initialized at compile time in the way static variables with constant initialization would be. which is what this all was originally about.

wintry agate
#

and ofcourse you can't have initialize something at compile time that's happening in runtime

#

so it all goes hand in hand

wintry agate
tender grove
#

my point was that here

int x = 42;

constexpr void fun()
{
    constexpr int var = 0;
}
````x` will be initialized at compile time while `var` cannot be initialized at compile time in the same way `x` would be.
#

(yes, the var instances inside invocations of fun that happen during constant evaluation will ofc be initialized at compile time)

wintry agate
#

yes it's incomplete cause it doesn't picture how fun is being utilized. it leaves uncertainly since constexpr functions != consteval that gotta at compile time

#

but i think we are both on the same page. just different ways of putting things earlier got me confused

#

but statics aren't guaranteed to be compile time are they?

tender grove
#

depends

#

if they have constant initialization, they are

wintry agate
#

then why would static constexpr be a thing for stuff that are guaranteed to run in compile time

tender grove
#

i'm not following

#

constexpr is not guaranteed to run at compile time

#

it just can run at compile time when needed

wintry agate
tender grove
#

you can have a static constexpr outside too

#

and yes, non-static members cannot be constexpr

#

because non-static members are not variables

#

they are created whenever the object is

#

it wouldn't make sense for those to be constexpr

wintry agate
#

Fair nuff. When would it make sense to use static constexpr outside?

tender grove
#

well, outside, the static would give it internal linkage

#

which is better done using an unnamed namespace

#

tbf, since const implies internal linkage anyways

#

that wouldn't really do anything

#

so yeah

#

i guess it doesn't really ever make a lot of sense actually

wintry agate
#

yea so static constexpr won't offer much over static const given static implies compile time anyways and the value being immutable

wintry agate
tender grove
#

no. static constexpr would not offer much over just constexpr. const is completely different.

tender grove
#

the reason for const implying internal linkage, afaik, is so that you could have const variables in headers without creating linker issues.

wintry agate
tender grove
#

i already gave you one

#

#1092566026262151380 message

final inlet
#

Iirc, constexpr just means a value/function can have a value at compile time so that they can be used for array sizes and template parameters, but there are no guarantees that any given constexpr variable is precomputed

#

Am I correct?

wintry agate
#

function works diff than value

#

function doesn't guarantee compile time. that's what consteval is for

wintry agate
tender grove
wintry agate
#

it's confusing cause it's called a compile-time constant and it doesn't refer to the initialization part. it could be interpreted as a variable being defined at compile time

#

but again, what benefit does initializing a runtime object with a compile-time constant bring VS a runtime object with a runtime value?

tender grove
#

constexpr mostly refers to the value, not to the object itself

tender grove
umbral halo
#

are constexpr variables guaranteed to have storage?

i.e can I take the address of a constexpr variable or is it like taking the address of a literal?

tender grove
#

yes you can

wintry agate
#

it's possible to initialize a runtime object with a compile time value?

tender grove
#

constant evaluation is only guaranteed to take place where a constant expression is required

umbral halo
#

Do you think it would be helpful to introduce decompilation of how constexpr works?

Qosvo how familiar are you with reading assembly? can you do the basics?

tender grove
#

just because something could be a constant expression doesn't mean that it actually is one. or that it is evaluated at compile time.

tender grove
#

ofc no sane compiler will recompute constant expressions at runtime

#

but it could do that and it would be conforming for it to do that.

#

also, compilers have been computing values at compile time for many decades before constexpr ever was a thing. constant folding is like compiler design 101.

#

constexpr is neither sufficient nor necessary for compile time evaluation to take place

wintry agate
#

then why/when would you use it? in your example, no way fun could be used in a compile time context since the function doesn't even meet the criteria to begin with so does using constexpr help in any way?

umbral halo
tender grove
#

constexpr serves one very particular purpose: being able to perform complex computations in places where it is required that they are actually performed at compile time at the C++ level.

umbral halo
#

let me give an example

#

the library fmt

#

it does absolute nonsense with templates (template meta programming) inorder to parse format strings at compile time

#

this makes it faster than printf

#

since it can ofload some of the processing to compile time

tender grove
#

for example: constant folding will generally happen much later in the compilation process. you can't rely on constant folding to compute the size of an array or smth.

wintry agate
umbral halo
#

constinit? right?

tender grove
wintry agate
#

so constexpr is there to ensure to just always initialize an object with a compilie time constant so it doesn't have to compute anything in runtime

wintry agate
umbral halo
#

yes / np

#

its complicated

wintry agate
#

consinit is not for locals

#

that's a diff story

#

plus its not a const

tender grove
#

it doesn't guarantee compiletime evaluation or initialization or anything.

wintry agate
#

then why is it called a compile time constant

tender grove
#

it's not?

wintry agate
#

it is

#

look up

tender grove
#

where?

#

the C++ specification says nothing about constexpr meaning that something is a compile time constant.

#

all this stuff about constexpr meaning "compile time" are just oversimplifications people like to make because the actual details are way complex and not easy to wrap your head around

wintry agate
#

we use the constexpr keyword instead of const in a variable’s declaration. A constexpr (which is short for “constant expression”) variable can only be a compile-time constant. If the initialization value of a constexpr variable is not a constant expression, the compiler will error.

tender grove
#

so they just say "oh it means compile time, now move along and look at this squirrel over there"

tender grove
#

where does this say that the value will never be computed not at compile time?

#

and "compile-time constant" is not a precise technical term.

wintry agate
#

you asked about where it's used and i showed you

tender grove
#

and i'm telling you that these are all just simplifications people make in order to make "explaining" constexpr easier

#

they are not a 100% accurate description of what constexpr actually is or does

#

far from it, in fact

wintry agate
#

lol

#

far from it uhm

tender grove
#

the accurate description of what constexpr actually is and does is found in the C++ standard

wintry agate
#

that's making one's life hard

tender grove
#

and you will find that the standard says nothing about compile-time constants and whatnot

#

it presents pages upon pages of rules which determine how stuff is supposed to behave

#

and what constexpr actually means is the behavior that ermerges from those rules interacting with the rest of the language

#

and i'm afraid this stuff is too complex to be condensed into a few simple words like "happens at compile time"

#

because if that was actually sufficient to fully characterize the feature, then there'd be no need to have pages upon pages of specification for it

#

there are lots of details and corner cases where the "happens at compile time" explanation simply doesn't work

#

a key point of constexpr was that you should be able to use the thing at compile or at runtime. you don't want to only be able to use it at compile time. that would solve part of the problem we're trying to solve (having to use a completely different subset of the language to do things at compile time) but at the same time introduce the exact reverse of the problem we were trying to solve as a new problem that then needs to be solved. that's why constexpr generally just means "can be used at compile time", not that it will always be evaluated at compile time.

#

but then there's the issue that maybe you'd want to do some stuff in that function that only works at runtime but would never happen at compile time. so you wouldn't want to disallow those things outright so that you can still cover both scenarios with the same code.

#
  • the halting problem is a thing
#

so you generally cannot know whether you can actually evaluate an arbitrary expression at compile time just by looking at it.

#

the only way to know is to actually try to evaluate it at compile time and see if that works.

#

but how do you know when you should try?

#

#

see? once you try to work out the details of how to actually make constexpr work, this turns out to be a lot more complex than one might initially think…

gritty saddle
#

Honestly this is a good read and seems accurate compared to most online tutorials

wintry agate
#

it talks about how const could be a compile time or runtime constant however to ensure a compile-time const, we can make use of constexpr

#

but like dot said, constexpr doesn't guarantee compile time evaluation

#

or I should say initialization

#

the link doesn't talk about how if a constexpr variable is used in a function, it doesn't guarantee that it's evaluted in compile-time

constexpr int foo()
{
  constexpr int v = 10;
  return v;
}```
my understanding is: `v` is a runtime object if `foo`  isn't run in a compile time (without constexpr in the caller). 
and `v` is a compile time object if `foo` is indeed run in a compile time
gritty saddle
#

Just like dot pointed out

wintry agate
#

we can enlist the compiler’s help to ensure we get a compile-time const where we expect one
this doesn't mean "may be"

gritty saddle
#

Well, we should read the whole thing:

When using const, our variables could end up as either a compile-time const or a runtime const, depending on whether the initializer is a compile-time expression or not. Because the definitions for both look identical, we can end up with a runtime const where we thought we were getting a compile-time const. In the previous example, it’s hard to tell if y is a compile-time const or a runtime const -- we’d have to look at the return value of getNumber() to determine.

Fortunately, we can enlist the compiler’s help to ensure we get a compile-time const where we expect one. To do so, we use the constexpr keyword instead of const in a variable’s declaration. A constexpr (which is short for “constant expression”) variable can only be a compile-time constant. If the initialization value of a constexpr variable is not a constant expression, the compiler will error.

wintry agate
#

the difference really being, const could be initialized with a runtime value however constexpr can't be hence a compiletime const

gritty saddle
#

"We get a compile-time const" does not mean that it is initialized at compile time, dot pointed that out (in this context, referring to the v object)

wintry agate
#

and you will find that the standard says nothing about compile-time constants and whatnot
he said compile-time const isn't a thing to begin with

gritty saddle
#

That's true, there's no concept of "compile-time"

wintry agate
#

then why does this link talk about compile time const?

gritty saddle
#

Doesn't make the page itself wrong, though, the concepts are all correct and well explained

wintry agate
#

and we bringing it back in again...

#

then how do you define a compile-time const

gritty saddle
#

It's like when we talk about mathematics. Sometimes you just say "x = y" without "rigorous terminology"

#

The thing itself is "correct", but it's not a rigurous definition

wintry agate
#

but loosely defined things can lead to only more confusion

#

what to you is a compile time const

gritty saddle
#

A value evaluated at compile time, I guess

#

I mean I think dot pointed out that the purpose of these tutorials not going into the very complex stuff is because it'd require 10 pages for explanations

#

But I don't see how the way the concept is explained in that page is confusing, honestly

wintry agate
#

it's not confusing but when you relate to our convo to what's in the page it gets a bit

gritty saddle
#

If you want the rigurous definition the standard / cpprefernce is available, I mean

wintry agate
#

also if you can't tell whether something is to be evaluted at compile time there's a type trait is_constant_evaluted or something which can be utilized

gritty saddle
#

Not sure what's that referred to

wintry agate
#

so you generally cannot know whether you can actually evaluate an arbitrary expression at compile time just by looking at it

#

the part that it's a complex topic

gritty saddle
wintry agate
#

dependent anyways

#

you could have control paths for when it's constant evaluated and when it's not using type trait. This is in response to

then there's the issue that maybe you'd want to do some stuff in that function that only works at runtime but would never happen at compile time. so you wouldn't want to disallow those things outright so that you can still cover both scenarios with the same code.

gritty saddle
#

What do you mean?

wintry agate
gritty saddle
#

I think I just misunderstood your doubts

wintry agate
gritty saddle
#

I don't get what you mean by "runtime" and "compiletime" object

wintry agate
#

and if something isn't technically correct, it shouldn't be referenced in official articles imo. I am referring to compile-time constant being technically invalid, apparently

wintry agate
gritty saddle
#

Yes it can

wintry agate
#

and can it ever be created in compile-time?

gritty saddle
#

Yeah I guess

wintry agate
#

there you go then. you have an answer to your question

gritty saddle
#

It depends on how it's called, I assume, but we can see a very dumb example

#

;asm -std=c++20

#include <optional>
#include <iostream>

constexpr int foo() {
    constexpr int x = 5;
    constexpr int y = 6;
    constexpr int u = x + y;
    return u;
}

int foo2() {
    constexpr int x = 5;
    constexpr int y = 6;
    constexpr int u = x + y;
    return u;
}

int main()
{
    int y = foo();
    int z = foo2();
}
thin blazeBOT
#
Assembly Output
foo2():
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-4], 5
  mov DWORD PTR [rbp-8], 6
  mov DWORD PTR [rbp-12], 11
  mov eax, 11
  pop rbp
  ret
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], 11
  call foo2()
  mov DWORD PTR [rbp-8], eax
  mov eax, 0
  leave
  ret
__static_initialization_and_destruction_0(int, int):
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], edi
  mov DWORD PTR [rbp-8], esi
  cmp DWORD PTR [rbp-4], 1
  jne .L7
  cmp DWORD PTR [rbp-8], 65535
  jne .L7
  mov edi, OFFSET FLAT:_ZStL8__ioinit
  call std::ios_base::Init::Init() [complete object constructor]
  mov edx, OFFSET FLAT:__dso_handle
  mov esi, OFFSET FLAT:_ZStL8__ioinit
  mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
  call __cxa_atexit
.L7:
  nop
  leave
  ret
_GLOBAL__sub_I_foo2():
  push rbp
  mov rbp, rsp
  mov esi, 65535
  mov edi, 1
  call __static_initialization_and_destruction_0(int, int)
  pop rbp
  ret

gritty saddle
#

foo() is completely elided

#

evaluation at compile time, then
foo2() isn't (note it doesn't have the constexpr at function level)

#

evaluation at runtime

#

But this example could be expanded to your example as well, I guess, same reasonings

wintry agate
#

what do you mean by elided here? it's not run? i don't see in asm

gritty saddle
#

all done in compile time

#

the compiler optimizes these all out, even if no optimization level used specifically

wintry agate
#

how's foo run in compile time given y in the caller isn't even constexprin the first place? It's not guaranteed so it may or may not. Wouldn't count on it

#

A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.

whereas this is guaranteed to run in compile time

constexpr int foo() 
{
     int x = 5;
     int y = 6;
     int u = ++x + y;
     return u;
}
constexpr int y = foo();
gritty saddle
wintry agate
gritty saddle
#

The compiler is able to optimize it all out I guess

#

The difference is only whether the function is or not "constexpr" itself here

wintry agate
#

yeah but that doesn't tell much; it's rather how it's used i beleive

#

i was referring to the locals in particular in foo2

#

that are constexpr

#

but the function itself isn't.

gritty saddle
#

Well you can see foo2() constexpr variables aren't evaluated at compile time

wintry agate
#

yeah so what good does using constexpr do for locals?

#

constexpr is a new C++11 keyword that rids you of the need to create macros and hardcoded literals. It also guarantees, under certain conditions, that objects undergo static initialization. It controls the evaluation time of an expression. By enforcing compile-time evaluation of its expression, constexpr lets you define true constant expressions that are crucial for time-critical applications, system programming, templates, and generally speaking, in any code that relies on compile-time constants
pepega
compile-time evaluation also means the object is compile-time isn't it?

constexpr int var = 10; 

the expression is what here? 10 or the whole line including var?

gritty saddle
#

yes 10

wintry agate
#

ok so here v could be created at runtime but would be initialized with a compile-time expression 10, yes?```cpp
constexpr int foo()
{
constexpr int v = 10;
return v;
}
int y = foo();

gritty saddle
#

Yeah

#

Well, now that I recheck here, it depends -- it may, or it may not

wintry agate
#

that's why I said could

gritty saddle
#

Ah, then yeah

wintry agate
#

so the idea would be to utilize constexpr when you're certain it's initialized with a constant expression? so v here and leave it to the compiler and function usage to make a decision?

gritty saddle
#

I'd say: whenever you can use the expression possibly at compile time, you could make the variable constexpr because why not

#

Whether the compiler does that optimization or not is something that you shouldn't always rely on. The advantage of using constexpr on v here is that you'd be able to use it for other constexpr variables, or anything that requires a constant expression, in these cases v can be used

#
constexpr int foo()
{
   int v = 10;
   constexpr int y = v;
  return v;
}
``` not allowed, `v` not constexpr, cannot be used as constant expressions -- *even though* the compiler might optimize it all out
wintry agate
gritty saddle
#

Exactly

#

Even though the compiler could optimize it all out and make compile time evaluations here, you still can't use "v" as constant expression

wintry agate
#

but this is fine since v is gonna be known regardless when y is being intiialized```cpp
constexpr int foo()
{
constexpr int v = 10;
int y = v;
return v;
}

gritty saddle
#

it's ok because "y doesn't require a constant expression"

wintry agate
#

yea but moreso v would be known by the time execution gets to y = v

#

so not an issue

gritty saddle
#

I guess that’s another way to see it, yes

thin blazeBOT
#
Assembly Output
foo2():
  mov eax, 11
  ret
main:
  xor eax, eax
  ret
_GLOBAL__sub_I_foo2():
  sub rsp, 8
  mov edi, OFFSET FLAT:_ZStL8__ioinit
  call std::ios_base::Init::Init() [complete object constructor]
  mov edx, OFFSET FLAT:__dso_handle
  mov esi, OFFSET FLAT:_ZStL8__ioinit
  mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
  add rsp, 8
  jmp __cxa_atexit

gritty saddle
#

here with optimizations on the compiler is able to evaluate all foo2 constexpr variables at compile time

#

Can we always rely on it? No

wintry agate
#

yea
for consteval is more restrictive version of constexpr in the sense that it only runs in compile-time no matter what. So all the constant rules still apply, right?

consteval int foo()
{
    int y = 10;
    y++;
    return y;
}

int main() 
{
    int z = foo();
    return 0;
}

even tho z isn't constexpr, foo still is evaluated at compile time?

gritty saddle
#

;asm -std=c++20

consteval int foo()
{
    int y = 10;
    y++;
    return y;
}

int main() 
{
    int z = foo();
    return 0;
}
thin blazeBOT
#
Assembly Output
main:
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-4], 11
  mov eax, 0
  pop rbp
  ret

gritty saddle
#

yes

#

but that doesnt mean you can use z as constant expression

#

;compile -std=c++20

consteval int foo()
{
    int y = 10;
    y++;
    return y;
}

int main() 
{
    int z = foo();
    constexpr int y = z;
    return 0;
}
thin blazeBOT
#
Compiler Output
<source>: In function 'int main()':
<source>:11:23: error: the value of 'z' is not usable in a constant expression
   11 |     constexpr int y = z;
      |                       ^
<source>:10:9: note: 'int z' is not const
   10 |     int z = foo();
      |         ^
Build failed
wintry agate
#

fair, but if you were to use constexpr on a variable in main, that would guarantee compile time evaluation no? or that will be left to the compiler too

gritty saddle
#

constexpr merely says: "the value of this variable can be used in constant expressions"

wintry agate
#

so no guarantee whatsoever?

constexpr int y{100};  // not guaranteed to run in compile time?
gritty saddle
#

there's no such guarantee as far as my understanding goes

#

Because again: if we watch my previous example, the constexpr variables aren't evaluated at compile time

#

(assuming no optimizations from the compiler)

wintry agate
#

i am referring to them used in main and not in a function

gritty saddle
#

I don't think it matters

wintry agate
#

why? it matters how you call your function. if the return value isn't constant evaluated, it's not guaranteed otherwise it is.

wintry agate
gritty saddle
#

(The example is dumb, forget it)

#

Well, no, as opposed to nothing lol

gritty saddle
#

I don't know how else to put this

#

making it constexpr ensures that its value might be evaluated at compile time

wintry agate
#

so standard doesn't provide any sort of guarantee whatsoever eh
i thought that was a selling point for constexpr over const which isn't guaranteed to be compile time evaluated either

gritty saddle
#

The point is that with constexpr, the expression initializing the constexpr variable MUST BE a constant expression

#

with const, it doesn't need to be

wintry agate
#

yup, fair

#

whether the object created & initialized at compile or runtime is upto the compiler

gritty saddle
#

I don't know whether "its up the compiler"

#

It's up to what we do with the code, I guess? Maybe that plays a role, I have no idea

#

constexpr int foo(int x) {
    return x + 5;
}

int main() {
    const int size = foo(4);
    constexpr int x = size * 2; // size and 2 are both constant expressions because we passed a literal value to foo, so foo(4) is also a constant expression
}
tender grove
gritty saddle
#

"size" here (although not constexpr directly) is usable in constant expression because it was initialized by the result of another constant expression

#

if we passed "zzz" to foo(int), this would fail

tender grove
#

the key thing to understand about constexpr is that it's not about guaranteeing that things are evaluated at compile time. the point of constexpr is making it possible for you to use code in places where using it is only possible if you can evaluate it at compile time.

#

for example: computing the size of an array, or some value to be used as a template argument

#

these places require that the values be known at compile time

#

constexpr makes it possible for you to run code to compute values that go into places where only things that are known at compile time can go

#

that's it

#

constexpr does not have any effect beyond that

gritty saddle
#

I don't know what the evaluation thing confusion is all about, constexpr just ensures that this works:

int main() {
    constexpr int x = 5; // if no constexpr, this fails!
    constexpr int size = x;
}
tender grove
#

tbf, in this particular case, const would suffice

#

but that's just a relic from a time before constexpr was a thing

gritty saddle
#

yeah as the example aboe I guess, but it gives the idea I think

tender grove
#

do the same with float and it won't work with just const

gritty saddle
wintry agate
#

or example: computing the size of an array
well here an argument can be made in favor of const

const std::size_t sz = 5;
int arr[sz]; ```
wintry agate
#

but i guess for certainty, constexpr shall be used specially when initializing from function returns

tender grove
wintry agate
#

exactly so i am not sure if it makes a good case for constexpr usage since const justifies too

tender grove
#

now make an array the size of which is to be computed via some complicated formula

#

and consider that maybe the size sometimes depends on runtime values, so then you want the same computation but computed at runtime to size a dynamic buffer.

#

now it would be great to have a way to just put that computation into a function and call it either to compute the array size, or to compute the size of the runtime buffer

#

it's the same calculation after all, used for the same purpose: sizing a buffer. the only difference is that you need to be able to use it to compute a value that can go into places only constant expressions can go.

wintry agate
tender grove
#

it doesn't matter. something that needs to pad and align stuff to 69 bytes for…reasons.

#

the point is: you don't always just have a number you know. often, that number depends on some other parameters and should be computed from those.

wintry agate
#

also the following doesn't fail for me

std::size_t n = 50;
    const std::size_t sz = n;
    int tab2[sz];
tender grove
#

yeah because you're using gcc, and gcc eats invalid C++ for breakfast

#

-pedantic-errors

wintry agate
tender grove
#

clang is gcc compatible

#

so it eats the exact same invalid C++ gcc eats

wintry agate
#

did you add -pedantic-errors?

tender grove
#

yes

wintry agate
#

certainly using constexpr for constants doesn't justify its usage alone since const could be used for it too specially given there's no compile-time guarantee anyways

tender grove
#

const only works for integral constants

#

it doesn't work for any other type

#

and, again, you can't run code to calculate the value, even with integral constants

wintry agate
#

like here you could use const
constexpr double PI = 3.14159265358979323846;

tender grove
#

and then that would not be usable in a constant expression

#

because the exception only covers integral and enumeration types

wintry agate
#

you prolly meant constexpr

tender grove
#

and it's not useable in a constant expression

tender grove
#
const int x = 42;
#

x is useable in a constant expression

#

because there's an old exception from the rules specifically to allow that

wintry agate
tender grove
#

that's not what i said

wintry agate
tender grove
#

yes, the context there was const only works for integers in the sense of making it useable in a constant expression

#

a const float is not useable in a constant expression

#

a const int is

wintry agate
tender grove
#

yes

#

x0 is not a constant expression

#

x00 would be

#

but this won't compile

#

because x0 is not a constant expression

wintry agate
#

so how does constexpr float work but const float isn't usable in constant expression?

tender grove
#

because the rules say so 🤷‍♂️

#

there's no reason why this particular const float x0 couldn't be made useable in a constant expression. but it wasn't because there's no point to it because we have constexpr.

#

the whole thing with const int x = 42; being useable in a constant expression is just some legacy stuff

#

there's no point in adding more of that when we don't even need it anymore in the first place since constexpr exists.

gritty saddle
#

So technically

#

And hypothetically

#

I could create a C++ compiler that always (no matter what) evaluates any expression during run time

#

…and it would be compliant?

#

myArray[foo()] would then work nonetheless, even if foo() was a constant expression, my compiler would still hypothetically evaluate it at run time

wintry agate
#

variable is usable in constant expressions at a point P if
the variable is
a constexpr variable, or
it is a constant-initialized variable
of const-qualified integral or enumeration type
prolly this?

tender grove
gritty saddle
#

‘Integral constant expressions’ on cppref Qosvo

gritty saddle
#

Assuming it doesnt evaluate everything at runtime, then?

#

foo() would still be a constant expression, etc

wintry agate
#

basically then the motivation behind constexpr becomes being able to initialize an object with a compile-time value which isn't guaranteed by const

wintry agate
gritty saddle
#

We are pushing the limits of standardese here pepekek

tender grove
gritty saddle
tender grove
#

if I try to create an array with the size being not a constant expression, your compiler is required to complain

gritty saddle
#

From my understanding it still is a constant expression, but my compiler decided not to evaluate it during translation

tender grove
#

it can still compile the program and then do whatever, the rules of C++ don't apply past that point

wintry agate
#

but we settled on that before that const would suffice?

tender grove
gritty saddle
#

Assume foo() is something like

constexpr int foo() { return 5; }

foo() is a constant expression

#

...but the compiler might wanna evaluate it during runtime, perhaps?

tender grove
#

due to the halting problem, the only way to know whether an expression is a constant expression is to try to evaluate it

wintry agate
#

it would depend on how it's used

#

if the return isn't a constant expression there's no guarantee

gritty saddle
#

in an array size, it would at least

#

because otherwise it would complain, right

tender grove
#

but yes, your compiler could evaluate it at compile time for the sole purpose of type checking the program, and then evaluate everything again at runtime.

wintry agate
gritty saddle
#

Nono, my doubt was something separate

tender grove
gritty saddle
#

Yeah my point is that it's weird to think an array's size could be evaluted at run time

#

even if, by the standard, it could technically (???)

#

it seems so

wintry agate
tender grove
#

no

#

it can run foo at runtime as well

#

it just has to run it once at compile time to check that the program is valid

wintry agate
#

what does this imply then

A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.

#

will only be

tender grove
#

but there's no requirement that it doesn't run it again at runtime

wintry agate
#

it really says WILL be and not could be

#

not leaving any option out

tender grove
#

that quote is not from the spec

#

so whatever it says is whatever it says

wintry agate
#

it's from a well followed article

#

learncpp

#

see, that's why there's a whole lotta confusion around

tender grove
#

well, it also says what I've been saying. a constexpr function will be evaluated at compile time when that's actually required, and for the purpose that it's required for. there's no requirement that the compiler remember the result. it could just as well evaluate it again at runtime later on.

#

it's just that no sane compiler will do that

#

but there's no guarantee that it won't.

gritty saddle
#

Is this for the type-check you discussed earlier?

tender grove
#

because it couldn't emit the required diagnostics otherwise

gritty saddle
#

Oh, makes sense

gritty saddle
#

It would be crazy, lol

#

Even though it would be

tender grove
# gritty saddle Oh, makes sense

it needs to emit a diagnostic if the size of an array is not a constant expression. it needs to know what the arguments of templates are in order to know what the program even is. doing those things requires evaluating constant expressions. but beyond that, it could do whatever. compile time evaluation is not required beyond the contexts where it's required.

#

the initializer of a constexpr variable must be a constant expression. the compiler must complain if it isn't. so it must evaluate the initializer of a constexpr variable whenever it sees one, to check that it's a constant expression. crucially, it doesn't have to keep the result. because that's not required. it could just reevaluate the value the variable is initialized to every time the variable is used in another constant expression.

gritty saddle
#

So that would still be compliant

tender grove
#

and, crucially, it could just reevaluate it again when the variable is initialized at runtime

gritty saddle
#

Technically

tender grove
#

yes

gritty saddle
#

It makes sense, somewhat

#

Why would I, as the standard, allow a compiler to possibly commit such a crime?

tender grove
#

except when the variable has static constant initialization ofc

#

though even that is tricky

#

because that just requires that the initialization happens before anything else. so it could technically evaluate that, e.g., when the program is loaded

#

it just has to happen before dynamic initialization before the first line of main is run

#

that brings us to the next thing

#

C++ doesn't actually have a concept of compile time vs run time

#

because there's no requirement that C++ is compiled in the traditional sense

gritty saddle
#

Yeah, I see in the standard it's referred to as "during translation"

#

But I guess that's open to interpretations, IDK

tender grove
#

yeah ^^

gritty saddle
#

Anyway we really pushed standardese with this post I swear

#

~~700 ~~ 900 messages for constexpr only

brazen vale
gritty saddle
thin blazeBOT
#
Compiler Output
<source>: In function 'int main()':
<source>:3:26: error: the value of 'f' is not usable in a constant expression
    3 |     constexpr float ff = f;
      |                          ^
<source>:2:17: note: 'f' was not declared 'constexpr'
    2 |     const float f = 3.14;
      |                 ^
Build failed
tender grove
wintry agate
tender grove
#

this doesn't compile?

#

it doesn't compile because you cannot "use const as a constant expression there"

wintry agate
#

i know it won't compile

tender grove
#

seriously?

gritty saddle
#

What you're asking then... makes no sense, Qosvo?

tender grove
#

yeah i'm quite confused as well

gritty saddle
#

constexpr allows the variable to be used in constant expressions, const doesn't except in rare cases

#

but its not its purpose

tender grove
#

you generally cannot use const objects in constant expressions

#

there's only one exception to that. apart from that one, you need constexpr.

gritty saddle
#

its sole purpose is to make the variable const, non-mutable, constexpr is not just that

wintry agate
#

just like how you can use cont for integral constants, constexpr doesn't make a good case alone. I was looking for examples along those lines where constexpr makes a good case .. something const couldn't. The float example is good but why wouldn't one just use make the float itself a constant expression rather than using const first and then evaluating as a constant expression

tender grove
#

wdym "make the float itself a constant expression"?

wintry agate
#

you coulda just done constexpr float ff = 3.14;

tender grove
#

YES BUT THAT ONLY WORKS BECAUSE CONSTEXPR IS A THING

#

so how is this not a case for constexpr?

#

you cannot do this any other way

#

there's literally no other way to make a float that's usable in a constant expression.

#

or anything else that's not an integer or enum

#

or what do you consider would "make a good case for constexpr". i'm not sure what better case for constexpr than examples of things that literally cannot be achieved without constexpr?

#
constexpr float pi = 3.14159265358979f;
wintry agate
tender grove
#

guaranteeing compiletime evaluation IS NOT THE PURPOSE OF CONSTEXPR

#

being able to use things in constant expressions is

wintry agate
#

the point of constexpr is making it possible for you to use code in places where using it is only possible if you can evaluate it at compile time.

tender grove
#

yes

wintry agate
#

if you can evaluate at compile-time which isn't much useful when it comes to comparing const vs constexpr here

tender grove
#

wat

#

being able to have things evaluated at compiletime is the sole purpose of constexpr

wintry agate
#

you say it's not the purpose of constexpr but earlier you mentioned it being the key point

tender grove
#

there are many contexts in C++ where you can only use things that can be evaluated at compile time

#

examples include sizes of array types or template arguments

wintry agate
#

yea but constexpr doesn't guarantee neither does const

#

so how's one diff than the other here

#

for a float example

tender grove
#

there is a key difference between it being possible that something can be evaluated at compile time and it being guaranteed that it is never not evaluated at compile time.

wintry agate
tender grove
wintry agate
tender grove
#

every word in that sentence is intentional

wintry agate
#

then the latter part implies it's guaranteed to evaluate at compile time? const?

#

nothing's guaranteed tho?

gritty saddle
#

So, see: there are many cases in which a "constant expression" is required. For example:

  • initializing other constexpr variables
  • builtin array sizes
  • template non type parameters
    etc

that's where you'll need "constexpr" because it will let you use the variable in such contextes

#
template<double x>
void foo() {}

int main() {
    const double x = 3; // ehhhh
    foo<x>();
};
tender grove
#

do you understand the difference between it being possible for something to happen and it being guaranteed that something never does not happen?

wintry agate
tender grove
#

constexpr allows for compiletime evaluation. but it does not guarantee compiletime evaluation.

wintry agate
#

neither does const

tender grove
#

const does neither of the two

#

with one exception

#

constexpr does one of the two

#

consteval does the other part

wintry agate
#

why are we referring to the latter part ; neither const guarantees nor does constexpr. Now the former part is allows for compile-time evaluation. What does that imply if not guaranteed?

tender grove
#

wat

#

at this point, i have no idea what on earth you're on about

wintry agate
#

const does neither of the two
const doesn't guarantee compiletime evaluation. agreed?

tender grove
#

i do not understand what is so difficult to understand about the difference between something being possible and something being guaranteed

wintry agate
#

nothing is guaranteed though?

tender grove
#

so?

narrow wave
#

playing on the lottery tickets makes it possible for you to win $1000000, it doesnt guarantee it

tender grove
#

guaranteeing is not the point of constexpr

#

making it possible is

wintry agate
#

then why are we bringing something up that's not guaranteed by either const or constexpr

tender grove
#

wat

gritty saddle
#

What if you view it this way:

  • const doesn't guarantee that the variable can be used in constant expressions
  • constexpr does guarantee that the variable can be used in constant expressions
#

Because that's ALL it is about

tender grove
#

i have no idea

#

we've tried basically this whole thread to explain that this is not what constexpr does

tender grove
wintry agate
tender grove
#

no guarantee no

#

there are certain places in C++ where a value must be known at compile time. in those places, you can only use things which are guaranteed to be possible to know at compile time.

gritty saddle
#

They think that "constant expression" implies "evaluated at compile time" for some reason

#

But I can see why, a lot of resources imply this

wintry agate
#

No I don't?

#

that's not what my confusion was

gritty saddle
#

Then I don't get what your confusion is at all

gritty saddle
#

How isn't it related to that, I don't get it

wintry agate
gritty saddle
#

You're constantly bringing the compile time evaluation

wintry agate
#

there's no guarantee that it will be run in compile time

gritty saddle
#

Yes, there isn't

wintry agate
#

i keep bringing it up cause i wanted to clarify the usage b/w const and constexpr

tender grove
#

tl;dr: just because something is constexpr does not mean it will be evaluated at compile time. it just means that it can be evaluated at compile time in case that's necessary.

wintry agate
#

where neither of the two guarantee that

gritty saddle
#

So you know that neither of them guarantee that

#

So why are you confused about it?

wintry agate
#

...as to what makes a good case for constexpr over const

tender grove
#

every use of constexpr

#

because const does not cover those

#

it's kinda like asking what makes a good use of integers over strings

wintry agate
#

every case also includes the following ```cpp
constexpr int val = 53;

#

but it can be achieved using const as well

tender grove
#

yes, in this one isolated case

gritty saddle
#

I'd say intent is a good thing here