When does a constexpr function get evaluated at compile time?
constexpr
functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well. A constant expression could be a literal (like 42
), a non-type template argument (like N
in template<class T, size_t N> class array;
), an enum
element declaration (like Blue
in enum Color { Red, Blue, Green };
, another variable declared constexpr, and so on.
They might be evaluated when all its arguments are constant expressions and the result is not used in a constant expression, but that is up to the implementation.
Constexpr functions not called at compile-time if result is ignored
It might be confusing, but constexpr functions should be called at compile time only in constexpr contexts (assignation to constexpr variable, use for array size or template parameter, ...).
In regular context, functions are called at runtime. Optimizer might resolve that function at compile time (as for any other functions following the as-if rule). constexpr
is indeed a good hint for optimization to happen.
You could argue that since constexpr functions cannot have side-effects
They can have side effect, see following valid example:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}
Demo
How to understand constexpr function has everything it needs to compute its result at compile-time?
But per my understanding, for the first case, the argc value also can't be known during compile-time
You're confusing "parameters" with "arguments".
Parameters are the things in the function declaration that represent stuff the user passes in. Arguments are the actual stuff the user passes in at the site of a particular function call.
argc
is an argument. The value of this may or may not be a constant expression, and if it is not, a constexpr
function is fine.
int
is used as the type for a parameter to add
. int
is a type which can be a constant expression. Now, not every int
value is a constant expression, but it is a type that could theoretically be one.
Therefore, add
can be a constexpr
function, because its parameter list uses types which can be constant expressions. This is about the nature of the function itself.
The type vector<T>
can never be a constant expression (in C++17, at any rate). As such, no constexpr
function can take one as a parameter. Nor can it use one internally.
Is it possible to test if a constexpr function is evaluated at compile time?
The technique listed works, but since it uses static_assert
it is not sfinae friendly. A better way (in theory, you'll see what I mean) to do this is to check whether a function is noexcept
. Why? Because, constant expressions are always noexcept, even if the functions are not marked as such. So, consider the following code:
template <class T>
constexpr void test_helper(T&&) {}
#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))
test_helper
is constexpr
, so it will be a constant expression as long as its argument is. If it's a constant expression, it will be noexcept
, but otherwise it won't be (since it isn't marked as such).
So now let's define this:
double bar(double x) { return x; }
constexpr double foo(double x, bool b) {
if (b) return x;
else return bar(x);
}
foo
is only noexcept
if the x
is a constant expression, and b
is true; if the boolean is false then we call a non constexpr
function, ruining our constexpr-ness. So, let's test this:
double d = 0.0;
constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));
std::cerr << x << y << z;
It compiles, great! This gives us compile time booleans (not compile failures), which can be used for sfinae, for example.
The catch? Well, clang has a multi-year bug, and doesn't handle this correctly. gcc however, does. Live example: http://coliru.stacked-crooked.com/a/e7b037932c358149. It prints "100", as it should.
Why is initialization of variable with constexpr evaluated at runtime instead of at compile time
By inspecting the generated assembly, we can confirm that in both cases G++ 7.5 computed the fib(32)
value at compile time:
movl $2178309, %esi
The reason G++ evaluates constexpr
context so fast is due to memoization which it performs when evaluating constexpr
and template
contexts.
Memoization completely kills fibonacci computational complexity by reducing it to O(N) complexity.
So why then is non-constexpr
evaluation so much slower? I presume that's a bug/shortcoming in the optimizer. If I try with G++ 8.1 or later, there's no difference in compilation times, so presumably it had already been addressed.
constexpr function gets value at compile time even though my variable is not constexpr
ipSourceAddress
does not get its value at compile time (as-if rule nonwithstanding).
ip
's value is not usable in a constant expression since it is not declared constexpr
and doesn't qualify for one of the other exceptions to the rule of lvalue-to-rvalue conversion in a constant expression. Therefore IPAddr::inet_pton<AF_INET>(ip)
is not a constant expression.
You can see this clearly by making ipSourceAddress
constexpr
(which auto
does not imply).
constexpr
on a variable requires initialization at compile-time and because the initializer is not a constant expression it will fail.
See godbolt.
I don't know how you come to a different conclusion.
Note however that the library does seem to require that the char
array passed to it is exactly as long as the string it contains (plus null-terminator). It fails if you hand it a longer array and outputs the value you are seeing.
See godbolt.
It seems the author intends the functions to only be called directly with string literals.
What is preventing compile time evaluation of this constexpr function?
You have set optimisation to level 1 in Godbolt! Try -O3
instead of -O1
.
Related Topics
How to Do the Equivalent of Memset(This, ...) Without Clobbering the Vtbl
Is There Any Difference Between "T" and "Const T" in Template Parameter
Multiplication of Two Integers in C++
Why Does This Code with '1234' Compile in C++
How to Use Std::Filesystem on Gcc 8
Variadic Template in VS 2012 (Visual C++ November 2012 Ctp)
How to Have Two Functions That Call Each Other C++
Static Constexpr Odr-Used or Not
Rotate an Image in C++ Without Using Opencv Functions
Rdrand and Rdseed Intrinsics on Various Compilers
Designated Initializers in C++20
Undefined Reference to 'Pthread_Key_Create' (Linker Error)
Why Is Clocks_Per_Sec Not the Actual Number of Clocks Per Second
Calling Assignment Operator in Copy Constructor
Checking Value Exist in a Std::Map - C++
How to Write Portable Code in C++
Reading and Writing a Std::Vector into a File Correctly
Cannot Get Opencv to Compile Because of Undefined References