0

Why doesn't defining a variable with auto keyword carry the constexpr'ness of the expression used to initialize it?


As an example, consider the following code:

#include <string_view>

constexpr std::string_view f() { return "hello"; }

static constexpr std::string_view g() {
    constexpr auto x = f(); // (*)
    return x.substr(1, 3);
}

int foo() { return g().length(); }

With GCC 10.2 and --std=c++20 -fsanitize=undefined -O3, this compiles into:

foo():
        mov     eax, 3
        ret

But if we remove the constexpr on line (*), we would get a 27-line program with a bunch of pointers, a long string constant etc.

Notes:

  • I marked this question C++20, but I have no reason to believe this behavior is different from C++11's.
  • This question is not about the example, it is about the general behavior of auto w.r.t. constexprness. The example simply shows that that GCC does not treat x as constexpr if we don't explicitly tell it to.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 3
    `auto` works the same was as templates do. Top level const is ignored. `constexpr` is never infered. – NathanOliver Oct 23 '20 at 12:40
  • 3
    `constexpr` isn't part of the type system, it isn't something that can be deduced. – Barry Oct 23 '20 at 12:59
  • constexpr expression is not (guaranteed to be) evaluted in compile-time out of a constexpr context. The effect of your sample is caused by compiler's optimization and it may not come with the default optimization level. Check this [sample](https://godbolt.org/z/Ya69TE). – VainMan Oct 23 '20 at 13:03
  • @Barry: But it is 'deduced', when you want to use an expression in a constexpr context. So why not carry this feature over the auto along with that type? – einpoklum Oct 23 '20 at 13:13
  • @VainMan: Ok, but why isn't GCC required to treat `auto x = f()` just like `constexpr auto x = f()`? – einpoklum Oct 23 '20 at 13:14
  • @NathanOliver: I don't quite understand what you mean. Can you elaborate? – einpoklum Oct 23 '20 at 13:15
  • 1
    If you have `template void foo(T)` and pass it a `const int`, `T` will be `int`, not `const int` as top level const is removed from the deduced type. Same thing happens with `auto`. It removes the `const`. – NathanOliver Oct 23 '20 at 13:17
  • @einpoklum I am not an expert in the area of language-lawyer. But I think one of the reason is that only the used branches of constexpr function are required to be constant experssions while other branches not. For example, `constexpr int f(int a) {return a == 0 ? 42 : time(nullptr);}` is legal. if `auto x = f(0)` is required to be "deduced" as a constant expression, it must know the function body. – VainMan Oct 23 '20 at 13:43
  • I get the same 3 line asm for `foo` with no `constexpr` here https://gcc.godbolt.org/z/Mn7xEh – Aykhan Hagverdili Oct 23 '20 at 13:54
  • @AyxanHaqverdili: Huh! Well, actually, it seems that's because of the undefined behavior sanitizer. Weird. – einpoklum Oct 23 '20 at 15:31

1 Answers1

5

auto is intended to enable type deduction, not a replacement for "everything useful you would have typed here". constexpr is not a part of an expression's type, and is thus ignored by auto (as opposed to const and volatile, which are part of an expression's type, and are deduced).


But if we remove the constexpr on line (*), we would get a 27-line program with a bunch of pointers, a long string constant etc.

That is a choice for your compiler. It has 100% of the information it needs to make that code go away. The fact that it didn't is not the C++ standard's concern.

This is a "quality of implementation" issue, not a standardization issue. If an implementation won't run as much of your code at compile-time as you desire, you can complain to them about it.

Remember: constexpr isn't meant to be a runtime optimization per-se. It's meant to allow you to write things that you otherwise couldn't write. Like std::get<g()>(some_tuple) or whatever. That code has to run at compile-time, since it's being used in a template parameter.


I'm not asking about some kind of deep deduction, only about the case of the function explicitly being constexpr.

Let's forget for a moment that auto is for type deduction and constexpr is not part of the type system. Let's focus instead on what if auto was supposed to deduce constexpr anyway. So what you want is for auto to only deduce constexpr if <expr> is specifically a function that is designated constexpr.

So let's look at some code:

auto x = constexpr_func();
auto y = constexpr_func() + 5;
auto z = constexpr_func() + constexpr_func();
auto w = constexpr_func_2() + constexpr_func_2();

Which of these variables are constexpr? If what you want is what we had, then x would be constexpr, but y would not. I personally would find this both surprising and annoying.

Worse, if we assume constexpr_func() returns an int, then z is also not constexpr. But if constexpr_func_2() returns a user-defined literal type that has a constexpr operator+, then w would be constexpr.

Isn't that all very weird? So I highly suspect that this is not what you really want.

What you really want is for auto x = <expr>; to deduce constexpr if constexpr auto x = <expr>; would be valid.

But really, that goes back to the original point. If you make a variable constexpr, that should mean you want it to be used in a place where being constexpr is required by some process. Given that fact, deducing constexpr makes no sense, because you should need it to be constexpr lest you get a compile error.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    "`constexpr` isn't meant to be a runtime optimization" this x100 – Caleth Oct 23 '20 at 14:06
  • I feel this answer is mostly restating the factual situation rather than the cause. What I'm wondering is why isn't `auto` carry the out-of-type-system information into the definition as well? Why should the compiler not be required to treat `auto x = constexpr_func();` as a constexpr variable? I'm not asking about some kind of deep deduction, only about the case of the function explicitly being constexpr. – einpoklum Oct 23 '20 at 15:17
  • 2
    @einpoklum Consider the different behavior you're suggesting for `auto a = 0;` and `int b = 0;` and how much code would break if `a` suddenly became `constexpr` here. – Barry Oct 23 '20 at 15:20
  • @einpoklum: "*What I'm wondering is why isn't auto carry the out-of-type-system information into the definition as well?*" Are you asking why a tool meant for type deduction doesn't deduce things that aren't part of the type system? Why should the compiler treat that specific case specially? – Nicol Bolas Oct 23 '20 at 15:21
  • @NicolBolas: Ok, now I think I have my answer. I will slightly edit your answer to reflect what you said in the comment. – einpoklum Oct 23 '20 at 15:34
  • Also, I don't see where I implied I expect constexpr to be a runtime optimization. – einpoklum Oct 23 '20 at 15:50
  • @einpoklum: It's the part in your question where you cited that the problem was the presence/absence of code based on the use of `constexpr`. The only reason to care about what assembly the compiler generates is if you care about the performance of the program. – Nicol Bolas Oct 23 '20 at 15:52
  • @NicolBolas: I used it only to "prove" that the compiler doesn't treat the variable as constexpr (in that, when you mark it as constexpr, it behaves differently). – einpoklum Oct 23 '20 at 16:02
  • 1
    @einpoklum: But it doesn't "behave differently"; it *compiles* differently. The behavior of the program is unchanged. The only reason to care about how it compiles is performance. – Nicol Bolas Oct 23 '20 at 16:03
  • @NicolBolas: I meant, the compiler behaves differently, not the program. – einpoklum Oct 23 '20 at 16:03