I'm just guessing here, but can it be because being std::string s = "abc"
an automatic variable and allocated in the stack at function begin (even if not yet constructed) breaks the constexpr
rules?
If I change the code to:
using namespace std::string_literals;
bool bar(std::string);
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
else {
//std::string ss = "abc"s;
return bar("abc"s);
}
return false;
}
as there is no need to allocate anything it compiles.
I explain my reasoning (and response to comments) here, as I need more space than in a comment.
As @StoryTeller-UnslanderMonica says, "guessing is a bad basis for answering questions".
Absolutely yes. That's why I begin saying just that: I'm guessing. And that have a reason.
I don't like to guess normally but I found this interesting and want to throw a thought to see if someone says I'm wrong (something I'm pretty ready to accept.)
But going to the point, literal type variables are normally stored at some read only memory data segment (unless they are numbers, those can be translated directly to ASM MOV/... instructions), not at stack.
If declared automatic (storing at stack):
Storage duration
All objects in a program have one of the following storage durations:
automatic storage duration. The storage for the object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local.
(Emphasis mine.)
So, even if declared after the if
, the storage is allocated and should be deallocated in any case (in the example shown by OP.)
In fact, if done like this:
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
const std::string ss = "abc"s;
return bar(ss);
}
the error is:
main.cc:15:16: error: call to non-‘constexpr’ function ‘std::__cxx11::basic_string<char> std::literals::string_literals::operator""s(const char*, std::size_t)’
why? I guess because, being automatic, "the storage for the object is allocated at the beginning of the enclosing code block" (beginning of the function) no matter the execution code path.
Moreover, if you declare it constexpr
, it introduces the destructor:
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
constexpr std::string ss = "abc"s;
return bar(ss);
}
error:
main.cc:19:32: error: temporary of non-literal type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} in a constant expression
constexpr std::string ss = "abc"s;
^~~~~~
In file included from /usr/include/c++/8/string:52,
from main.cc:2:
/usr/include/c++/8/bits/basic_string.h:77:11: note: ‘std::__cxx11::basic_string<char>’ is not literal because:
class basic_string
^~~~~~~~~~~~
/usr/include/c++/8/bits/basic_string.h:77:11: note: ‘std::__cxx11::basic_string<char>’ has a non-trivial destructor
main.cc: In instantiation of ‘constexpr bool foo(T) [with T = int]’:
main.cc:25:29: required from here
main.cc:19:27: error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘ss’ is not literal
constexpr std::string ss = "abc"s;
I think the key is: ‘std::__cxx11::basic_string<char>’ has a non-trivial destructor
.
so the theoretical call to the destructor is taken in account before the execution code path.
Why?
Because "the storage for the object is allocated at the beginning of the enclosing code block".
The following:
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
return bar("abc"s);
}
creates a temporary:
main.cc:19:15: error: call to non-‘constexpr’ function ‘bool bar(std::__cxx11::string)’
return bar("abc"s);
but
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
} else {
return bar("abc"s);
}
return false;
}
creates the temporary only if the execution path goes to the else
(which is not the case.)
As said, this is a guess, but I think a based guess, not just a blind try.
Again, I'm convinced this depends on compiler implementation. I'm by no means a C++ standard expert, but I haven't been able to find this explicit case in any document.
I've run the program in gdb
to see if it enters in the foo
function:
bool bar(std::string);
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
} else {
//std::string ss = "abc"s;
return bar("abc"s);
}
return false;
}
int main() {
//constexpr bool cb1 = foo(-1); // error as expected
constexpr bool cb2 = foo(1); // this is also an error now :(
cout << "Bool: " << cb2 << endl;
return 0;
}
it links without bar
being defined so...
manuel@desktop:~/projects$ g++ -Wall -Wextra -g main.cc -o main --std=gnu++2a -Wpedantic && time ./main
Bool: 1
real 0m0,002s
user 0m0,000s
sys 0m0,002s
manuel@desktop:~/projects$ gdb ./main
GNU gdb (Debian 8.2.1-2+b3) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./main...done.
(gdb) b main
Breakpoint 1 at 0x117d: file main.cc, line 27.
(gdb) r
Starting program: /home/manuel/projects/main
Breakpoint 1, main () at main.cc:27
27 constexpr bool cb2 = foo(1); // this is also an error now :(
(gdb) s
29 cout << "Bool: " << cb2 << endl;
(gdb) s
Bool: 1
31 return 0;
(gdb) s
32 }
(gdb) q
A debugging session is active.
Inferior 1 [process 18799] will be killed.
Quit anyway? (y or n) y