【问题标题】:Why does `auto` not adopt the constexpr'ness of its initializing expression?为什么`auto`不采用其初始化表达式的constexpr'ness?
【发布时间】:2021-02-06 12:38:52
【问题描述】:

为什么用auto 关键字定义变量时不带有用于初始化它的表达式的constexpr'ness?


例如,考虑以下代码:

#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 的一般行为。 constexprness。该示例仅表明,如果我们没有明确告知 GCC 不会将 x 视为 constexpr

【问题讨论】:

  • auto 的工作方式与模板相同。顶级 const 被忽略。 constexpr 永远不会被推断出来。
  • constexpr 不是类型系统的一部分,它不是可以推断的。
  • constexpr 表达式不会(保证)在编译时超出 constexpr 上下文进行评估。您的示例的效果是由编译器的优化引起的,它可能没有默认的优化级别。检查这个sample
  • @Barry:但是当您想在 constexpr 上下文中使用表达式时,它是“推导的”。那么为什么不把这个特性和那个类型一起带到 auto 上呢?
  • 如果你有 template &lt;typename T&gt; void foo(T) 并传递给它 const intT 将是 int,而不是 const int,因为顶级 const 已从推导类型中删除。 auto 也会发生同样的事情。它删除了const

标签: c++ type-inference constexpr c++20 auto


【解决方案1】:

auto 旨在启用类型推断,而不是替代“您在此处键入的所有有用信息”。 constexpr 不是表达式类型的一部分,因此会被 auto 忽略(与 constvolatile 相反,它们是表达式类型的一部分,并且是推导的)。


但是如果我们删除第 (*) 行的 constexpr,我们将得到一个 27 行的程序,其中包含一堆指针、一个长字符串常量等。

这是您的编译器的选择。它拥有使该代码消失所需的 100% 的信息。事实上,它不是 C++ 标准的关注点。

这是一个“实施质量”问题,而不是标准化问题。如果一个实现在编译时不能像你希望的那样运行你的代码,你可以向他们投诉。

记住:constexpr 本身并不意味着运行时优化。它的目的是让你写出你无法写出的东西。像std::get&lt;g()&gt;(some_tuple) 或其他什么。该代码必须在编译时运行,因为它在模板参数中使用。


我不是在问某种深度演绎,只是关于函数显式为 constexpr 的情况。

让我们暂时忘记auto 用于类型推断,constexpr 不是类型系统的一部分。让我们转而关注如果auto 无论如何都应该推断出constexpr 会怎样。因此,如果&lt;expr&gt; 是专门指定为constexpr 的函数,那么您想要auto 推导出constexpr

那么让我们看一些代码:

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

这不是很奇怪吗?所以我高度怀疑这不是你真正想要的。

如果constexpr auto x = &lt;expr&gt;; 有效,您真正想要的是auto x = &lt;expr&gt;; 推导出constexpr

但实际上,这又回到了原点。如果您创建一个变量constexpr,那应该意味着您希望它在某个进程需要constexpr 的地方使用。鉴于这一事实,推断 constexpr 没有任何意义,因为您应该需要它是 constexpr,以免出现编译错误。

【讨论】:

  • "constexpr 并不意味着是运行时优化" this x100
  • 我觉得这个答案主要是在重申事实情况而不是原因。我想知道为什么auto 不将超出类型系统的信息也带入定义中?为什么不要求编译器将auto x = constexpr_func(); 视为 constexpr 变量?我不是在问某种深度演绎,只是关于函数显式为 constexpr 的情况。
  • @einpoklum 考虑一下您为auto a = 0;int b = 0; 建议的不同行为,以及如果a 在这里突然变成constexpr 会破坏多少代码。
  • @einpoklum:“我想知道为什么不自动将超出类型系统的信息也带入定义中?”你在问为什么一个用于类型推断的工具不能推断出不属于类型系统的东西?为什么编译器要特别对待这种特殊情况?
  • @einpoklum:但它并没有“表现不同”;它编译不同。程序的行为没有改变。关心它如何编译的唯一原因是性能。
猜你喜欢
  • 2020-12-22
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 2015-07-05
  • 1970-01-01
相关资源
最近更新 更多