简体   繁体   English

constexpr函数中的复合赋值:gcc与clang

[英]Compound assignment in constexpr function: gcc vs. clang

template<class A, class B> constexpr int f(A a, B b) {
    a /= b;
    return a;
}

constexpr int x = f(2, 2);   // a, b: int
constexpr int y = f(2., 2.); // a, b: double
constexpr int z = f(2, 2.);  // a: int, b: double //<-- BOOM!
constexpr int w = f(2., 2);  // a: double, b: int

int main() {}

The code doesn't compiled in clang, it produces the following diagnostic: 代码不在clang中编译,它产生以下诊断:

error: constexpr variable 'z' must be initialized by a constant expression

MSVC crashed (according to godbolt ) and gcc works fine. MSVC坠毁(根据godbolt )并且gcc工作正常。 If a /= b is simply replaced by a = a / b then everybody accepts it. 如果a /= b简单地被a = a / b替换,那么每个人都接受它。 Why? 为什么?

Who is right? 谁是对的? Seems it's related to implicit narrowing conversion, but then why a = a / b works? 似乎它与隐式缩小转换有关,但是为什么a = a / b有效?

This is simply a clang bug, if we look at compound assignment [expr.ass]p7 it is equivalent to assignment where E1 is evaluated only once: 这只是一个铿锵的错误,如果我们看一下复合赋值[expr.ass] p7它相当于只评估一次E1赋值:

The behavior of an expression of the form E1 op= E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once. E1 op = E2形式的表达式的行为等同于E1 = E1 op E2,除了E1仅被评估一次。 In += and -=, E1 shall either have arithmetic type or be a pointer to a possibly cv-qualified completely-defined object type. 在+ =和 - =中,E1应具有算术类型或者是指向可能由cv限定的完全定义的对象类型的指针。 In all other cases, E1 shall have arithmetic type. 在所有其他情况下,E1应具有算术类型。

If we look in the restrictions on constant expression function requirement in [dcl.constexpr]p3 we don't have any restrictions on assignment: 如果我们查看[dcl.constexpr] p3中对常量表达式函数要求的限制,我们对赋值没有任何限制:

The definition of a constexpr function shall satisfy the following requirements: constexpr函数的定义应满足以下要求:

  • (3.1) its return type shall be a literal type; (3.1)其返回类型应为字面类型;
  • (3.2) each of its parameter types shall be a literal type; (3.2)每个参数类型应为文字类型;
  • (3.3) its function-body shall not contain. (3.3)其功能体不得含有。
  • (3.3.1) an asm-definition, (3.3.1)asm-definition,
  • (3.3.2) a goto statement, (3.3.2)goto声明,
  • (3.3.3) an identifier label ([stmt.label]), (3.3.3)标识符标签([stmt.label]),
  • (3.3.4) a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed. (3.3.4)非文字类型或静态或线程存储持续时间的变量的定义,或者不执行初始化的定义。
    [ Note: A function-body that is = delete or = default contains none of the above. [注意:函数体= = delete或= default不包含上述任何内容。 — end note ] - 结束说明]

and nothing in [expr.const] adds restrictions for this specific case. [expr.const]中没有任何内容为此特定情况添加限制。

I contacted Richard Smith offline and he agreed this was a bug and said: 我离线联系理查德史密斯,他同意这是一个错误并说:

Yes, it's a bug; 是的,这是一个错误; that code is not correctly taking into account that the LHS could need conversion to floating point before the computation. 该代码未正确考虑LHS在计算之前可能需要转换为浮点数。

Filed clang bug report and MSVC bug report 提交的clang bug报告MSVC错误报告

I've committed a patch to clang, which should fix the clang bug. 我已经提交了一个 clang 补丁 ,它应该修复clang bug。

Some clang internal details: 一些铿锵的内部细节:

In clang, the evaluation of constant expression is mostly handled in lib/AST/ExprConstant.cpp . 在clang中,常量表达式的评估主要在lib / AST / ExprConstant.cpp中处理 In particular, compound assignment of integer is handled by CompoundAssignSubobjectHandler::found(APSInt &Value, QualType SubobjType) . 特别是,整数的复合赋值由CompoundAssignSubobjectHandler::found(APSInt &Value, QualType SubobjType) Before my patch, this function incorrectly rejected any non-integer RHS: 在我的补丁之前,此函数错误地拒绝了任何非整数RHS:

    if (!SubobjType->isIntegerType() || !RHS.isInt()) {
      // We don't support compound assignment on integer-cast-to-pointer
      // values.
      Info.FFDiag(E);
      return false;
    }

My patch fixes this by adding a branch for the RHS.isFloat() case. 我的补丁通过为RHS.isFloat()案例添加一个分支来解决这个问题。

Note that similar problem does not happen when LHS is a floating-point and RHS is an integer, even though CompoundAssignSubobjectHandler only handles the float <op>= float case, because the RHS is always promoted to a floating-point in this case. 请注意,当LHS是浮点且RHS是整数时,类似的问题不会发生,即使CompoundAssignSubobjectHandler只处理float <op> = float情况,因为在这种情况下RHS总是被提升为浮点数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM