问题描述
为什么用auto
关键字定义的变量不带有用于初始化变量的表达式的constexpr
'性质?
作为示例,请考虑以下代码:
#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(); }
使用GCC 10.2和--std=c++20 -fsanitize=undefined -O3
,将此compiles转换为:
foo():
mov eax,3
ret
但是,如果我们删除(*)行上的constexpr,我们将得到一个27行的程序,其中包含一堆指针,一个长字符串常量等。
注意:
- 我将此问题标记为C ++ 20,但我没有理由相信这种行为与C ++ 11的不同。
- 这个问题不是关于示例的 ,而是关于
auto
w.r.t.的一般行为。constexpr
ness。该示例只是表明,如果我们没有明确告知x,则GCC不会将x视为constexpr
。
解决方法
auto
用于启用类型推导,而不是“您在此处键入的所有有用内容”的替代。 constexpr
不是表达式类型的一部分,因此被auto
忽略(与const
和volatile
相对,它们是表达式类型的一部分,它们是推导)。
但是,如果我们删除(*)行上的constexpr,我们将得到一个27行的程序,其中包含一堆指针,一个长字符串常量等。
这是您的编译器的选择。它具有使该代码消失所需的100%信息。事实并非如此,这并不是C ++标准关注的问题。
这是“实施质量”问题,而不是标准化问题。如果某个实现在编译时无法按照您的期望运行太多代码,则可以向他们抱怨。
请记住:constexpr
本身并不是运行时优化。它的意思是允许您编写本来无法写的东西。像std::get<g()>(some_tuple)
之类的。该代码必须在编译时运行,因为它已在模板参数中使用。
我不是在问某种深度推论,而只是在关于函数显式为constexpr的情况。
让我们暂时忘记auto
用于类型推导,而constexpr
不是类型系统的一部分。让我们集中讨论如果应该auto
推断出constexpr
会怎样。因此,如果auto
是专门指定为constexpr
的函数,那么<expr>
只能推断出constexpr
。{p}
所以让我们看一些代码:
auto x = constexpr_func();
auto y = constexpr_func() + 5;
auto z = constexpr_func() + constexpr_func();
auto w = constexpr_func_2() + constexpr_func_2();
这些变量中的哪个是constexpr
?如果您想要的是我们所拥有的,那么x
将是constexpr
,而y
不会。我个人会觉得这既令人惊讶又烦人。
更糟糕的是,如果我们假设constexpr_func()
返回int
,那么z
也不是constexpr
。但是,如果constexpr_func_2()
返回具有constexpr operator+
的用户定义文字类型,那么w
将是 constexpr
。
那不是很奇怪吗?因此,我高度怀疑这不是您真正想要的。
您真正想要的是让auto x = <expr>;
推断constexpr
是否有效。
但实际上,这可以追溯到最初的观点。如果您创建变量constexpr auto x = <expr>;
,则意味着您希望在某个过程需要constexpr
的地方使用它。鉴于这一事实,推论constexpr
没有任何意义,因为您应该需要为constexpr
,以免出现编译错误。