我只是在这里猜测,但这可能是因为 std::string s = "abc" 是一个自动变量并在函数开始时分配到堆栈中(即使尚未构造)违反了 constexpr 规则?
如果我将代码更改为:
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;
}
因为它编译时不需要分配任何东西。
我在这里解释我的推理(以及对 cmets 的回应),因为我需要比评论更多的空间。
正如@StoryTeller-UnslanderMonica 所说,“猜测是回答问题的糟糕基础”。
绝对是的。这就是为什么我开始这么说的原因:我猜。这是有原因的。
我不喜欢正常猜测,但我觉得这很有趣,想想想是否有人说我错了(我已经准备好接受了。)
但言归正传,文字类型变量通常存储在一些只读内存数据段中(除非它们是数字,否则可以直接转换为 ASM MOV/... 指令),而不是堆栈。
如果声明为自动(存储在堆栈中):
存储时间
程序中的所有对象都具有以下存储期限之一:
自动存储期限。对象的存储在封闭代码块的开头分配,并在结尾处释放。除了声明为 static、extern 或 thread_local 的对象外,所有本地对象都有此存储持续时间。
(强调我的。)
因此,即使在if 之后声明,存储空间也已分配并且在任何情况下都应该被释放(在 OP 显示的示例中。)
事实上,如果这样做:
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
const std::string ss = "abc"s;
return bar(ss);
}
错误是:
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)’
为什么? 我猜因为自动,“对象的存储是在封闭代码块的开头分配的”(函数的开头),无论执行代码路径如何.
而且,如果你声明它constexpr,它会引入析构函数:
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
constexpr std::string ss = "abc"s;
return bar(ss);
}
错误:
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;
我认为关键是:‘std::__cxx11::basic_string<char>’ has a non-trivial destructor。
因此在执行代码路径之前考虑了对析构函数的理论调用。
为什么?
因为“对象的存储空间是在封闭代码块的开头分配的”。
以下内容:
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
return bar("abc"s);
}
创建一个临时的:
main.cc:19:15: error: call to non-‘constexpr’ function ‘bool bar(std::__cxx11::string)’
return bar("abc"s);
但是
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
} else {
return bar("abc"s);
}
return false;
}
仅在执行路径转到else 时创建临时(不是这种情况。)
如前所述,这是一个猜测,但我认为是一个基于的猜测,而不仅仅是盲目尝试。
再次,我确信这取决于编译器的实现。我绝不是 C++ 标准专家,但我无法在任何文档中找到这个明确的案例。
我已经在gdb中运行了程序,看是否进入foo函数:
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;
}
它在没有定义bar 的情况下链接...
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