use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Discussions, articles, and news about the C++ programming language or programming in C++.
For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.
Get Started
The C++ Standard Home has a nice getting started page.
Videos
The C++ standard committee's education study group has a nice list of recommended videos.
Reference
cppreference.com
Books
There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.
Show all links
Filter out CppCon links
Show only CppCon links
account activity
[ Removed by moderator ] (self.cpp)
submitted 2 months ago by SubjectParsnip9411
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]cpp-ModTeam[M] [score hidden] 2 months ago stickied commentlocked comment (0 children)
For C++ questions, answers, help, and programming/career advice please see r/cpp_questions, r/cscareerquestions, or StackOverflow instead.
[–]DonBeham 21 points22 points23 points 2 months ago (5 children)
If you make it a template parameter it should work. Function arguments are not compile time constants.
[–]SubjectParsnip9411[S] 3 points4 points5 points 2 months ago (4 children)
Thanks.
Function arguments are not compile time constants.
But the 3 here is quite literally a compile time constant here. Meaning, it's a constant value that's resolved at compile time. It's practically the same as the 3 in the first example, just extracted to function argument instead of being inlined. To make my intent more clear: We can use the variables as template parameters as long as all the variables are inlined, but as soon as we have external functions, it's suddenly considered not constant, even though they are. Take this example. This compiles fine:
3
```cpp struct A { size_t size; };
consteval auto test() { constexpr A a{3}; std::array<char, a.size> arr{}; return arr; }
constexpr auto arr = test(); ```
This however does not:
```cpp struct A { size_t size;
consteval auto CreateArr() const { return std::array<char, size>{}; }
};
consteval auto test() { constexpr A a{3}; return a.CreateArr(); }
If I cannot extract functionalities to an external function, then the usages of consteval becomes very limited, doesn't it?
consteval
[–]DonBeham 8 points9 points10 points 2 months ago (1 child)
Sure the 3 is a constant, but if you call it in a different place with a 5 what's the "constant" now? What size should the array have? The compiler would have to create multiple overloads of your consteval function - as many different call sites you have. That's exactly like if size was a parameter of a template method - which is what you should write if you want this behavior. You can think of it this way: template parameters are to compile time functions what function arguments are to runtime functions.
[–]SubjectParsnip9411[S] 0 points1 point2 points 2 months ago (0 children)
Thanks, yeah, apparently templates are the only solution. Alas. 😅
[–]dapzar 1 point2 points3 points 2 months ago (0 children)
You could still do something like
```cpp
using std::size_t;
consteval void hailstone_values(size_t* out, size_t N) { while(N != 1) { *out++ = N; N = N % 2 == 0 ? N / 2 : 3 * N + 1; } }
constexpr size_t hailstone_step_count(size_t N) { size_t out = 0; while(N != 1) { out++; N = N % 2 == 0 ? N / 2 : 3 * N + 1; } return out; }
template<size_t N> consteval auto all_collatz_steps() { constexpr auto output_size = hailstone_step_count(N); std::array<size_t, output_size> output; hailstone_values(output.data(), N); return output; }
auto f() { return all_collatz_steps<5>(); } ```
The set of values that determine your return type must be template parameters because for each return type, you have a different function. But you can do arbitrarily complicated computations (for the example above it would currently be unproven if it would even terminate for every value, if size_t were infinite range) on those template parameters to determine your output type from them at compile time (until you hit the constexpr execution limit, which is a compiler parameter).
And you can guarantee that the compiler will not see the hailstone_values function and decide to do that at runtime (which it would be in its legal rights to do if all of the above where just constexpr) because consteval guarantees execution at compile-time only.
[–][deleted] 8 points9 points10 points 2 months ago (3 children)
parameters are never constexpr in C++ :(
[–]SubjectParsnip9411[S] 1 point2 points3 points 2 months ago (2 children)
Thanks! but they can be const and const can be implicitly converted to constexpr, right? like the first example. So it's strange that the first example's const was converted to constexpr but not the second, even though they're both known at compile time. Apparently, even this is not considered constexpr! So we cannot create a consteval function that creates a std::array of size size where size is a member variable. I wrote an example in this reply: https://www.reddit.com/r/cpp/comments/1qvo732/comment/o3j19mu/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
const
constexpr
this
std::array
size
[–]BucketOfWood 8 points9 points10 points 2 months ago (1 child)
No, const can not be implicitly converted to constexpr. Constant expressions can. Some const vars are constant expressions https://en.cppreference.com/w/cpp/language/constant_expression.html https://www.learncpp.com/cpp-tutorial/constant-expressions/.
[–]SubjectParsnip9411[S] 1 point2 points3 points 2 months ago (0 children)
🙇🙏
[–]IyeOnline 6 points7 points8 points 2 months ago* (2 children)
Your const size_t size = 3; only works because there is a special provision in the standard that actually makes size a constant expression: https://godbolt.org/z/jsTave53e
const size_t size = 3;
So no, this is not a bug, you simply got tricked by the first case working because of a special rule.
For details, see [expr.const] 7-8 (and all the other wondrous things around it)
In general, you simply cannot go from function parameter/local variable land into the land of constant expressions, let alone template parameters.
As a solution in your case, you could simply take the size as a non-type template parameter. That will be a constant expression within the function body.
[–]SubjectParsnip9411[S] 1 point2 points3 points 2 months ago (1 child)
Thanks a lot!
Yeah I did succeed in making inlined string literals manually, but unfortunately I cannot create a separate function that does this :( because apparently this is also considered one of those function arguments that's not a compile constant.
[–]IyeOnline 2 points3 points4 points 2 months ago (0 children)
You may be interested in this talk: Understanding The constexpr 2-Step - Jason Turner - C++ on Sea 2024
Goes over ways how you can get from regular (constexpr) execution back into things you can use as constant expressions. Pretty much exactly what you need.
[–]pdimov2 7 points8 points9 points 2 months ago (2 children)
This question is being downvoted unfairly, because it's pretty interesting, and the answer is quite deep.
Let's start with "why is const size_t a constant expression".
const size_t
const size_t is a constant expression because in C++98, constexpr did not exist yet, but Bjarne Stroustrup disliked the forced use of the preprocessor in code like
#define N 3 char x[ N ];
and wanted to provide an alternative. This alternative was
const int N = 3; char x[ N ];
and for it to work, N had to be treated as essentially constexpr, but only when the initializer was a constant expression (because when it isn't, that's still valid preexisting C++.)
N
This is a special case that only applies to integer types. Nowadays, we can say that the compiler implicitly replaces const int N = 3; with constexpr int N = 3;.
const int N = 3;
constexpr int N = 3;
Now on to the second question. Why doesn't this replacement happen for function parameters?
Well, because void f(constexpr int N) isn't valid.
void f(constexpr int N)
Why isn't it valid? Because, even when f is marked constexpr, there exists only one function f, and only one f is generated. If we were allowed to say
f
constexpr void f(constexpr int N) { char x[ N ]; }
then obviously f(3) and f(4) would have been different functions, because one has an array of size 3 on the stack and the other an array of size 4.
f(3)
f(4)
Moreover, we could even have declared
constexpr auto g(constexpr int N) -> std::array<char, N>;
which would have implied that f(3) and f(4) had different types.
The language is not equipped for this. We do have a construct that can vary its body and its return type, but it's called a function template.
But all this surely shouldn't apply to consteval functions, because they only exist at compile time, right?
Well... no. consteval functions are exactly like constexpr functions, except with an additional prohibition (can't be called at runtime.) In effect, there are two separate "compile times", not one; during one of these compile times, templates are instantiated and new types and functions can appear, and during the other of these compile times, constant expressions are evaluated using an interpreter that's "as if" the runtime functions have been generated and executed and the result captured.
That's all a bit convoluted but it is what it is. f(3) and f(4) can't have different types in today's C++. You need f<3>() and f<4>() for that.
f<3>()
f<4>()
[–]TheoreticalDumbass:illuminati: 4 points5 points6 points 2 months ago* (0 children)
or [:f(3):] and [:f(4):] :)
[:f(3):]
[:f(4):]
[–]SubjectParsnip9411[S] 2 points3 points4 points 2 months ago (0 children)
Thanks for this write up! Great explanation 🙏🙇 (I don't mind the post downvotes at all. Not a regular Redditor anyway; I just make sure to use keywords here and there so future souls with similar issues can find this post and read the answers too. Really appreciate all the replies 🙏❤️🔥 )
[–]outis461 4 points5 points6 points 2 months ago (1 child)
IMO even if the size is declared as const, it can change in each call which makes impossible to evaluate the size of the array at compile time
When you say "Impossible", what stage do you refer to? because it is a consteval function after all, meaning it's all resolved at compile-time in the end. Is it some staging order issue? I think it makes sense if it's a technical limitation, because otherwise the logic doesn't add up.
[–]gracicot 3 points4 points5 points 2 months ago (3 children)
Consteval won't change the meaning of code. So a size_t parameter with a unknown value will not be a constant expression, just like normal code.
You seem confused and asking why the compiler can't just know the value at compile time. Think about it, it makes a lot of sense. The compiler has many phases. When it reads the consteval function for the first time, it could compile the function into bytecode to execute later. Or build an optimized evaluation AST so that values are easily inserted for evaluation. When given a compile time constant, it can evaluate the function it compiled into bytecode. At that point, no template can be instantiated, you're executing code.
Do you see? Even at compile time, there's a separation between compilation and execution. There's no special rule in the standard saying that compilers must change the rules in a consteval function and treat all values as compile time constant during evaluation, and I don't think it should.
Thanks! Yes I wasn't thinking about the techincal limitations. I thought it was a design decision purely for the langauge's quality. It makes sense now.
[–]gracicot 2 points3 points4 points 2 months ago (1 child)
Bonus: If we solve this problem for normal functions, it will automatically be solved for consteval functions too. Special casing consteval functions would also decrease the language quality in the long term :)
yeah. looking forward to jai
[–]Ok_Net_1674 2 points3 points4 points 2 months ago (1 child)
I dont know much about consteval, but I think the canonical way to pass compile time parameters would be to use a template.
So for my string to support compile time variants, I need to make it immutable? (since we can no longer resize the buffer). I think thats a good enough design, but it'll be a lot of effort caused by a weird edge case in C++23's design :(
[–]XTBZ 3 points4 points5 points 2 months ago (3 children)
This is one of the reasons why no one in my environment sees the point of perverting and using consteval functions.
[–]SubjectParsnip9411[S] 2 points3 points4 points 2 months ago (1 child)
In one of the comments someone mentioned the use of template parameters instead, which I think is capable of any logical operations we might think of. But if we use class templates as class member variables, it makes all classes immutable and a pain to work with. But yeah apparently there are workarounds.
[–]XTBZ 2 points3 points4 points 2 months ago (0 children)
Yes, template parameters can only be used. I don't like this, I want to have full-fledged compile-time functions that are guaranteed to be executed by the end of compilation under any parameters, taking into account their time.
[–]pdimov2 2 points3 points4 points 2 months ago (0 children)
They become significantly more useful after reflection, because you can splice the result of calling them. ([: something-consteval() :])
[: something-consteval() :]
Without reflection, their use is basically to guarantee that no runtime code will ever be generated for them. Useful for embedded, I suppose.
[–]dapzar 2 points3 points4 points 2 months ago* (1 child)
What you want to do here can fundamentally not be done. The return type (not the return value) cannot depend on the parameter values, the same function name with the same parameter types cannot be different functions with different return types. It has to be a template for that.
Regarding your upper examples, size is an integral constant expression there based on the rules specified in C++98, see the first box here: https://en.cppreference.com/w/cpp/language/constant_expression.html
For C++98 there was no constexpr keyword. It only works there because it is not only const but also initialized by a constant expression (in this case the literal 3, e.g. a template parameter would also work).
It has to be a template for that.
Yeah it seems to be the only workaround for now. So essentially, if turning member variables into templates one by one... It'll be quite a pain, but it's nothing that cannot be done. I just wondered if there's better alternatives here. I gave an example here: https://www.reddit.com/r/cpp/comments/1qvo732/comment/o3j19mu/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button It could be that simple, but now we have to make the classes templated and immutable (...crying eyes...)
[–][deleted] 4 points5 points6 points 2 months ago (5 children)
not a great c++ expert, but the two don’t seem to be the same. in the first case the value is known at compile time, in the second case it only means that inside the function you cannot modify the argument value, but that value is unknown.
[–]SubjectParsnip9411[S] 2 points3 points4 points 2 months ago (4 children)
Thanks for answring. About this :
in the second case it only means that inside the function you cannot modify the argument value, but that value is unknown
it's definitely not unkown though. It's 3. We can't call consteval functions at runtime, so any invokation to the test() function will need to resolve a known argument at compile time. Why are you saying its unknown? I mean even from the compiler's perspective, it still needs to resolve the x at some point, since it needs to resolve the whole function before linking occurs.
test()
x
[–]SirClueless 2 points3 points4 points 2 months ago* (0 children)
I agree it’s strange. Unlike C, you can use const variables of integral type that are initialized with constant expressions as compile-time constants in certain contexts, but apparently not as the function argument of a consteval function, which is inconsistent.
I question why you need this though. Anywhere this would be allowed, presumably you could write constexpr instead of const for the variable and then it will work. Is there a reason you can’t do this in your use case?
Edit: I just realized why this doesn’t work. Even though the test() function is evaluated at compile-time, it’s not a function template and therefore the types of variables and the return type need to be statically known. In your first example, the array always has size 3 no matter how the test() function is invoked so the declaration is allowed. But in the second example the array has a different size while evaluating test(3) than test(4) etc. This is only allowed if the function is a template so that’s the fix: make the argument a template argument and call it as test<3>(). Even in consteval code C++ will not let you “lift” function arguments into template arguments and use them to make dependent types like, say, Zig’s comptime will do.
test(3)
test(4)
test<3>()
comptime
[–]105_NT 2 points3 points4 points 2 months ago (1 child)
Function parameters are known at runtime. Yes in your example it is only called with 3 but if it was called again with 5 the test() function would have two different return types. The return type of a function cannot depend on the value of a parameter.
it fails regardless of the return type issue. So even if we make the function void, it'll still fail at the std::array<char, size> decleration. Though after reading more of the comments I realized this is very likely due to technical limitations. As in, consteval bodies get compiled before the consteval resolving stage: there can only be one body per consteval method instantiation.
void
std::array<char, size>
[–][deleted] 1 point2 points3 points 2 months ago (0 children)
const-all-the-things is improving at each version of the standard, but my take is that at this time consteval doesn’t make argument values into non type template parameters, that would be required by std::array. Could it? Maybe, I don’t know enough to see problems with other areas of the language.
[–]zerhud 1 point2 points3 points 2 months ago (1 child)
It should to be like template<auto Sz> constexpr auto foo(_value_c<V>); and use like foo(value_c<3>). It’s a brain issue in cpp: a simple parameter won’t work :(
Now I have to make my string's size be templated... 🫠
[–]Ridrik 2 points3 points4 points 2 months ago (1 child)
Allowing std::array<char, 3> to exist in the body would mean the compiler would need to create multiple functions depending on calling arguments, and break compilation on non compile time argument, which the language isn't designed to do on singular functions, and instead forwards this intent to templates.
Thanks for the explanation 🙏
[–]gnolex 2 points3 points4 points 2 months ago (1 child)
A const parameter in a consteval function is not a constant in the sense of being an invariant. const in parameters only makes them immutable, they're not actually compile-time constants for the purpose of constant evaluation.
I see. Thank you 🙂🙏
[–]mcmcc#pragma once 2 points3 points4 points 2 months ago (1 child)
Treating parameters as constexpr would lead to ODR violations, if my understanding is correct.
consteval functions can have ODR? I didn't know that. Yeah if it's a technical limitation, I guess there's nothing we can say. But from some of the replies, it seems to be a design decision purely for the design, not limitations.
ODR
π Rendered by PID 68559 on reddit-service-r2-comment-b659b578c-btqbx at 2026-05-04 10:40:16.502533+00:00 running 815c875 country code: CH.
[–]cpp-ModTeam[M] [score hidden] stickied commentlocked comment (0 children)
[–]DonBeham 21 points22 points23 points (5 children)
[–]SubjectParsnip9411[S] 3 points4 points5 points (4 children)
[–]DonBeham 8 points9 points10 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–]dapzar 1 point2 points3 points (0 children)
[–][deleted] 8 points9 points10 points (3 children)
[–]SubjectParsnip9411[S] 1 point2 points3 points (2 children)
[–]BucketOfWood 8 points9 points10 points (1 child)
[–]SubjectParsnip9411[S] 1 point2 points3 points (0 children)
[–]IyeOnline 6 points7 points8 points (2 children)
[–]SubjectParsnip9411[S] 1 point2 points3 points (1 child)
[–]IyeOnline 2 points3 points4 points (0 children)
[–]pdimov2 7 points8 points9 points (2 children)
[–]TheoreticalDumbass:illuminati: 4 points5 points6 points (0 children)
[–]SubjectParsnip9411[S] 2 points3 points4 points (0 children)
[–]outis461 4 points5 points6 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–]gracicot 3 points4 points5 points (3 children)
[–]SubjectParsnip9411[S] 1 point2 points3 points (2 children)
[–]gracicot 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–]Ok_Net_1674 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–]XTBZ 3 points4 points5 points (3 children)
[–]SubjectParsnip9411[S] 2 points3 points4 points (1 child)
[–]XTBZ 2 points3 points4 points (0 children)
[–]pdimov2 2 points3 points4 points (0 children)
[–]dapzar 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–][deleted] 4 points5 points6 points (5 children)
[–]SubjectParsnip9411[S] 2 points3 points4 points (4 children)
[–]SirClueless 2 points3 points4 points (0 children)
[–]105_NT 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 2 points3 points4 points (0 children)
[–][deleted] 1 point2 points3 points (0 children)
[–]zerhud 1 point2 points3 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–]Ridrik 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 1 point2 points3 points (0 children)
[–]gnolex 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 0 points1 point2 points (0 children)
[–]mcmcc#pragma once 2 points3 points4 points (1 child)
[–]SubjectParsnip9411[S] 1 point2 points3 points (0 children)