为什么`auto`不采用其初始化表达式的constexpr'性?

问题描述

为什么用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忽略(与constvolatile相对,它们是表达式类型的一部分,它们是推导)。


但是,如果我们删除(*)行上的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,以免出现编译错误。