【问题标题】:Non-constexpr variant member call compiling inside constexpr class member function with condition - why?带有条件的 constexpr 类成员函数内的非 constexpr 变体成员调用编译 - 为什么?
【发布时间】:2020-10-15 12:41:12
【问题描述】:
#include <variant>

struct S {
    constexpr auto f() -> void {
        // deleting the next line creates an error
        if(std::holds_alternative<int>(m_var))
            m_var.emplace<double>(5.0);
    }
    std::variant<int, double> m_var;
};

int main() {
    return 0;
}

std::variant 有一个非constexpr 成员函数emplace()。一般来说,您不能在 constexpr 函数中使用它。但是,如果您通过在该类型上使用 std::holds_alternative() 的条件包围该调用,则可以。还有其他 constexpr 函数,只要它们是该类中的成员函数。

我很难理解发生了什么。我的第一反应是说这是一个错误。这个条件不可能比没有条件更 constexpr。但也许这还为时过早。任何人都可以对此有所了解吗?为什么emplace() 不是 constexpr 而(相等类型)赋值是?

编辑:也许扩展一下:一种猜测是所涉及的变体的构造函数和析构函数可能是非常数的,这就是为什么emplace 等不是。但有趣的是,即使您明确滥用非 constexpr 构造函数,您也可以使用这样的条件将函数编译为 constexpr。这使该论点无效。

天箭:here.

【问题讨论】:

标签: c++ c++17 constexpr


【解决方案1】:

实际上,您无需深入研究std::variant 即可对此进行推理。这主要是关于常量表达式的工作原理。 constexpr 函数必须以允许在常量表达式中求值的方式定义。对于某些参数,我们是否遇到不能出现在常量表达式中的东西并不重要,只要对于其他参数,我们获得一个有效的常量表达式。标准中明确提到了这一点,并举例说明

[dcl.constexpr]

5 对于 constexpr 函数或 constexpr 构造函数, 既不是默认值也不是模板,如果不存在这样的参数值 函数或构造函数的调用可能是 核心常量表达式的求值子表达式,或者,对于 构造函数,某个对象的常量初始化器 ([basic.start.static]),程序格式错误,没有诊断 必需的。 [ 示例:

constexpr int f(bool b)
  { return b ? throw 0 : 0; }           // OK
constexpr int f() { return f(true); }   // ill-formed, no diagnostic required

struct B {
  constexpr B(int x) : i(0) { }         // x is unused
  int i;
};

int global;

struct D : B {
  constexpr D() : B(global) { }         // ill-formed, no diagnostic required
                                        // lvalue-to-rvalue conversion on non-constant global
};

 — 结束示例 ]

看看f(bool) 是一个有效的constexpr 函数吗?即使throw 表达式可能不会在常量表达式中求值,它仍然可以出现constexpr 函数中。只要不进行不断的评估就没有问题。

如果有没有组参数可以在常量表达式中使用constexpr 函数,则程序格式错误。这种格式错误的程序不需要诊断,因为仅从函数定义中检查这种情况通常是难以处理的。然而,它是无效的 C++,即使编译器没有引发错误。但在某些情况下,它可以被检查,因此编译器可能不得不提出诊断。

您的f 没有条件属于此类非良构构造。不管f如何被调用,它的执行都会导致调用emplace,而emplace不能出现在常量表达式中。但它很容易检测到,所以你的编译器会告诉你这是一个问题。

带有条件的第二个版本不再无条件调用emplace。现在是有条件的。条件本身依赖于constexpr 函数,因此它不会立即形成错误。一切都取决于函数的参数(包括this)。所以它不会立即引发错误。

【讨论】:

  • 那么成功的 constexpr 编译根本不涉及任何保证,曾经吗?由于它的格式不正确,但标准不需要错误,因此要靠运气?我说对了吗?
  • @Basti - 不,肯定有保证。如果一个常量表达式被求值并试图“执行”一个被禁止的构造,你将得到一个诊断。该规范说,在绝对没有评估将导致常量表达式的情况下,程序已经无效。 NDR 部分之所以存在,是因为仅从定义中检查此类错误的难度很大。它基本上使编译器编写者免于尝试解决停止问题。
  • 当评估确定时,但我的意思只是一个简单的函数定义。即使 no 评估可能永远是 const,那么也不存在 保证 错误,对吧?我最近在stackoverflow.com/questions/64220243 遇到了这个问题,该函数可能——使用任何 T——永远不可能是 constexpr 并且仍然可以正常编译。
  • @Basti - 在这种情况下,不,没有标准的强制要求来获得诊断。然而,这并不取决于机会。这只是 QoI 的问题,可能会有所不同。
  • 当然,现在知道了。不过,不需要报告错误的概念似乎很奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-10
  • 2016-07-29
  • 1970-01-01
  • 2015-06-03
相关资源
最近更新 更多