非 constexpr 变体成员调用在 constexpr class 成员函数内编译,有条件 - 为什么?
Non-constexpr variant member call compiling inside constexpr class member function with condition - why?
#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 函数,只要它们是 class.
中的成员函数
我无法理解发生了什么。我的第一反应是说这是一个错误。该条件不可能比根本没有条件更 constexpr。但也许这还为时过早。任何人都可以阐明这一点吗?为什么 emplace()
不是 constexpr 而(等类型)赋值是?
编辑:也许要扩展一点:一种猜测是所涉及变体的构造函数和析构函数可能是非 constexpr,这就是为什么 emplace
等不是。但有趣的是,即使您显式滥用非 constexpr 构造函数,您也可以使用这样的条件将函数编译为 constexpr。这使该论点无效。
神箭:here.
您实际上不需要深入研究 std::variant
就可以对此进行推理。这主要是关于常量表达式如何工作的。 constexpr
函数的定义方式必须允许在常量表达式中求值。对于某些参数我们是否 运行 变成不能出现在常量表达式中的东西并不重要,只要对于其他参数我们获得有效的常量表达式即可。标准中明确提到了这一点,例如
[dcl.constexpr]
5 For a constexpr function or constexpr constructor that is
neither defaulted nor a template, if no argument values exist such
that an invocation of the function or constructor could be an
evaluated subexpression of a core constant expression, or, for a
constructor, a constant initializer for some object
([basic.start.static]), the program is ill-formed, no diagnostic
required. [ Example:
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
};
— end example ]
看看 f(bool)
如何成为一个有效的 constexpr
函数?即使 throw
表达式可能不会在常量表达式中求值,它仍然可以 出现 在 constexpr
函数中。只要持续评估达不到就没问题。
如果 没有 组参数可以在常量表达式中使用 constexpr
函数,则程序为 ill-formed。这种 ill-formed 程序不需要诊断,因为仅从函数定义中检查此条件通常是很棘手的。然而,它是无效的 C++,即使编译器没有引发错误。但在某些情况下,它可以被检查,因此编译器可能有义务提出诊断。
您的 f
没有 条件属于此类 ill-formed 结构。不管f
怎么调用,它的执行都会导致调用emplace
,它不能出现在常量表达式中。但它很容易检测到,因此您的编译器会告诉您这是一个问题。
你的第二个版本,带有条件,不再无条件地调用 emplace
。现在是有条件的。条件本身依赖于 constexpr
函数,因此它不会立即 ill-formed。一切都取决于函数的参数(包括 this
)。所以它不会立即引发错误。
#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 函数,只要它们是 class.
我无法理解发生了什么。我的第一反应是说这是一个错误。该条件不可能比根本没有条件更 constexpr。但也许这还为时过早。任何人都可以阐明这一点吗?为什么 emplace()
不是 constexpr 而(等类型)赋值是?
编辑:也许要扩展一点:一种猜测是所涉及变体的构造函数和析构函数可能是非 constexpr,这就是为什么 emplace
等不是。但有趣的是,即使您显式滥用非 constexpr 构造函数,您也可以使用这样的条件将函数编译为 constexpr。这使该论点无效。
神箭:here.
您实际上不需要深入研究 std::variant
就可以对此进行推理。这主要是关于常量表达式如何工作的。 constexpr
函数的定义方式必须允许在常量表达式中求值。对于某些参数我们是否 运行 变成不能出现在常量表达式中的东西并不重要,只要对于其他参数我们获得有效的常量表达式即可。标准中明确提到了这一点,例如
[dcl.constexpr]
5 For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required. [ Example:
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 };
— end example ]
看看 f(bool)
如何成为一个有效的 constexpr
函数?即使 throw
表达式可能不会在常量表达式中求值,它仍然可以 出现 在 constexpr
函数中。只要持续评估达不到就没问题。
如果 没有 组参数可以在常量表达式中使用 constexpr
函数,则程序为 ill-formed。这种 ill-formed 程序不需要诊断,因为仅从函数定义中检查此条件通常是很棘手的。然而,它是无效的 C++,即使编译器没有引发错误。但在某些情况下,它可以被检查,因此编译器可能有义务提出诊断。
您的 f
没有 条件属于此类 ill-formed 结构。不管f
怎么调用,它的执行都会导致调用emplace
,它不能出现在常量表达式中。但它很容易检测到,因此您的编译器会告诉您这是一个问题。
你的第二个版本,带有条件,不再无条件地调用 emplace
。现在是有条件的。条件本身依赖于 constexpr
函数,因此它不会立即 ill-formed。一切都取决于函数的参数(包括 this
)。所以它不会立即引发错误。