取以下合法代码:
bool bar();
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
return bar();
}
int main() {
//constexpr bool cb1 = foo(-1); // error as expected because it would attempt to call bar()
constexpr bool cb2 = foo(1); // ok
}
Run Code Online (Sandbox Code Playgroud)
因此,只要我们没有在编译时评估上下文中遇到非 constexpr 代码路径,我们的 constexpr 就形成了良好的格式。整洁的!
但是,如果我应用相同的实际概念,但碰巧在条件代码路径中包含非文字类型,例如std::string,则标准说不:
#include <string>
bool bar(std::string);
template <class T>
constexpr bool foo(T t) {
if (t>0) {
return true;
}
std::string s = "abc";
return bar(s);
}
int main() {
//constexpr bool cb1 = foo(-1); // error as expected
constexpr bool cb2 = foo(1); // this is also an error now :(
}
Run Code Online (Sandbox Code Playgroud)
这背后的原理是什么?为什么不惜一切代价使用 std::string 是非法的,即使它实际上从未被构造(或销毁)?
额外问题:那么为什么以下合法:https : //godbolt.org/z/L3np-u (上面略有变化,没有定义 std::string)?!
我只是在这里猜测,但这是否是因为作为std::string s = "abc"一个自动变量并在函数开始时在堆栈中分配(即使尚未构造)违反了规则constexpr?
如果我将代码更改为:
\nusing namespace std::string_literals;\n\nbool bar(std::string);\n\ntemplate <class T>\nconstexpr bool foo(T t) {\n if (t>0) {\n return true;\n }\n else {\n //std::string ss = "abc"s;\n return bar("abc"s);\n }\n return false;\n}\nRun Code Online (Sandbox Code Playgroud)\n因为不需要分配它编译的任何东西。
\n我在这里解释我的推理(以及对评论的回应),因为我需要比评论更多的空间。
\n正如@StoryTeller-UnslanderMonica 所说,“猜测是回答问题的糟糕基础”。
\n绝对没错。这就是为什么我开始这么说:我猜测。这是有原因的。
\n我通常不喜欢猜测,但我发现这很有趣,并且想思考一下是否有人说我错了(我已经准备好接受这一点。)
\n但说到重点,文字类型变量通常存储在某些只读存储器数据段中(除非它们是数字,否则它们可以直接转换为 ASM MOV/... 指令),而不是存储在堆栈中。
\n如果声明为自动(存储在堆栈中):
\n\n\n储存期限
\n程序中的所有对象都具有以下存储持续时间之一:
\n自动存储时间。对象的存储在封闭代码块的开头分配,并在末尾释放。所有本地对象都有这个存储持续时间,除了那些声明为 static、extern 或 thread_local 的对象。
\n
(强调我的。)
\n因此,即使在 后声明if,存储也会被分配,并且在任何情况下都应该被释放(在 OP 所示的示例中)。
事实上,如果这样做:
\ntemplate <class T>\nconstexpr bool foo(T t) {\n if (t>0) {\n return true;\n }\n const std::string ss = "abc"s;\n return bar(ss);\n}\nRun Code Online (Sandbox Code Playgroud)\n错误是:
\nmain.cc:15:16: error: call to non-\xe2\x80\x98constexpr\xe2\x80\x99 function \xe2\x80\x98std::__cxx11::basic_string<char> std::literals::string_literals::operator""s(const char*, std::size_t)\xe2\x80\x99\nRun Code Online (Sandbox Code Playgroud)\n为什么?我想是因为,无论执行代码路径如何,“对象的存储都是自动分配在封闭代码块的开头”(函数的开头)。
\n此外,如果你声明它constexpr,它会引入析构函数:
template <class T>\nconstexpr bool foo(T t) {\n if (t>0) {\n return true;\n }\n constexpr std::string ss = "abc"s;\n return bar(ss);\n}\nRun Code Online (Sandbox Code Playgroud)\n错误:
\nmain.cc:19:32: error: temporary of non-literal type \xe2\x80\x98const string\xe2\x80\x99 {aka \xe2\x80\x98const std::__cxx11::basic_string<char>\xe2\x80\x99} in a constant expression\n constexpr std::string ss = "abc"s;\n ^~~~~~\nIn file included from /usr/include/c++/8/string:52,\n from main.cc:2:\n/usr/include/c++/8/bits/basic_string.h:77:11: note: \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 is not literal because:\n class basic_string\n ^~~~~~~~~~~~\n/usr/include/c++/8/bits/basic_string.h:77:11: note: \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 has a non-trivial destructor\nmain.cc: In instantiation of \xe2\x80\x98constexpr bool foo(T) [with T = int]\xe2\x80\x99:\nmain.cc:25:29: required from here\nmain.cc:19:27: error: the type \xe2\x80\x98const string\xe2\x80\x99 {aka \xe2\x80\x98const std::__cxx11::basic_string<char>\xe2\x80\x99} of \xe2\x80\x98constexpr\xe2\x80\x99 variable \xe2\x80\x98ss\xe2\x80\x99 is not literal\n constexpr std::string ss = "abc"s;\nRun Code Online (Sandbox Code Playgroud)\n我认为关键是:\xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 has a non-trivial destructor。
因此理论上对析构函数的调用是在执行代码路径之前考虑的。
\n为什么?
\n因为“对象的存储是在封闭代码块的开头分配的”。
\n下列:
\ntemplate <class T>\nconstexpr bool foo(T t) {\n if (t>0) {\n return true;\n }\n return bar("abc"s);\n}\nRun Code Online (Sandbox Code Playgroud)\n创建一个临时的:
\nmain.cc:19:15: error: call to non-\xe2\x80\x98constexpr\xe2\x80\x99 function \xe2\x80\x98bool bar(std::__cxx11::string)\xe2\x80\x99\n return bar("abc"s);\nRun Code Online (Sandbox Code Playgroud)\n但
\ntemplate <class T>\nconstexpr bool foo(T t) {\n if (t>0) {\n return true;\n } else {\n return bar("abc"s);\n }\n return false;\n}\nRun Code Online (Sandbox Code Playgroud)\n仅当执行路径转到else(情况并非如此)时才创建临时文件。
正如我所说,这是一个猜测,但我认为这是一个有基础的猜测,而不仅仅是盲目的尝试。
\n再次,我确信这取决于编译器的实现。我绝不是 C++ 标准专家,但我在任何文档中都找不到这种明确的案例。
\n我已经运行该程序以gdb查看它是否进入该foo函数:
bool bar(std::string);\n\ntemplate <class T>\nconstexpr bool foo(T t) {\n if (t>0) {\n return true;\n } else {\n //std::string ss = "abc"s;\n return bar("abc"s);\n }\n return false;\n}\n\nint main() {\n //constexpr bool cb1 = foo(-1); // error as expected\n constexpr bool cb2 = foo(1); // this is also an error now :(\n\n cout << "Bool: " << cb2 << endl;\n \n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n它链接没有bar被定义所以......
manuel@desktop:~/projects$ g++ -Wall -Wextra -g main.cc -o main --std=gnu++2a -Wpedantic && time ./main\nBool: 1\n\nreal 0m0,002s\nuser 0m0,000s\nsys 0m0,002s\nmanuel@desktop:~/projects$ gdb ./main\nGNU gdb (Debian 8.2.1-2+b3) 8.2.1\nCopyright (C) 2018 Free Software Foundation, Inc.\nLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.\nType "show copying" and "show warranty" for details.\nThis GDB was configured as "x86_64-linux-gnu".\nType "show configuration" for configuration details.\nFor bug reporting instructions, please see:\n<http://www.gnu.org/software/gdb/bugs/>.\nFind the GDB manual and other documentation resources online at:\n <http://www.gnu.org/software/gdb/documentation/>.\n\nFor help, type "help".\nType "apropos word" to search for commands related to "word"...\nReading symbols from ./main...done.\n(gdb) b main\nBreakpoint 1 at 0x117d: file main.cc, line 27.\n(gdb) r\nStarting program: /home/manuel/projects/main \n\nBreakpoint 1, main () at main.cc:27\n27 constexpr bool cb2 = foo(1); // this is also an error now :(\n(gdb) s\n29 cout << "Bool: " << cb2 << endl;\n(gdb) s\nBool: 1\n31 return 0;\n(gdb) s\n32 }\n(gdb) q\nA debugging session is active.\n\n Inferior 1 [process 18799] will be killed.\n\nQuit anyway? (y or n) y\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
1076 次 |
| 最近记录: |