How does consteval influence evaluation of default arguments?

Viewed 210

In the code below, if here() stops being consteval (either full RT, or constexpr), then line() is the line of invocation of f() inside main(). But with consteval it's the definition of f(). Where does this discrepancy come from?

#include <experimental/source_location>
#include <iostream>

consteval std::experimental::source_location here(
    std::experimental::source_location loc = std::experimental::source_location::current())
{
   return loc;
}

void f(const std::experimental::source_location& a = here())
{
   std::cout << a.line() << std::endl; // will either print 17, or 10
}

int main()
{
   f();
}

Godbolt link

1 Answers

Here is my understanding:

The default arguments are substituted at call site and evaluated at each call. See the following code as example:

#include <iostream>

int count() {
    static int counter = 0;
    return ++counter;
}

void foo(int value = count())
{
    std::cout << value << "\n";
}

int main()
{
   foo();
   foo();
}

See in godbolt

The output is

1
2

Which proves count() has been called twice, and the main() body is effectively equivalent to :

foo(count());
foo(count());

Now let's go back to your example. When here() is not consteval, then your call to f() is equivalent to f(here()) which in turn is equivalent to f(here(std::experimental::source_location::current())) and this will return the line of the call to f(), which is 17.

However, if here() is consteval, when the compiler reads the declaration of f(), it must immediately evaluate here() which returns a certain std::experimental::source_location whose line() equals 10 at this point (let's call it default_location for the purpose of the explanation), therefore when you call f(), the default argument is already evaluated to default_location, and the call is effectively equivalent to f(default_location)

Related