if constexpr中的not-constexpr变量– lang与GCC [英] not-constexpr variable in if constexpr – clang vs. GCC

查看:104
本文介绍了if constexpr中的not-constexpr变量– lang与GCC的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

struct A{
    constexpr operator bool()const{ return true; }
};

int main(){
    auto f = [](auto v){ if constexpr(v){} };
    A a;
    f(a);
}

clang 6接受该代码,GCC 8拒绝该代码:

clang 6 accepts the Code, GCC 8 rejects it with:

$ g++ -std=c++17 main.cpp
main.cpp: In lambda function:
main.cpp:6:37: error: 'v' is not a constant expression
  auto f = [](auto v){ if constexpr(v){} };
                                     ^

谁是正确的,为什么?

当我为每个引用获取参数时,两个都拒绝代码:

When I take the parameter per reference, both reject the code:

struct A{
    constexpr operator bool()const{ return true; }
};

int main(){
    auto f = [](auto& v){ if constexpr(v){} };
    constexpr A a;
    f(a);
}

用clang 6编译:

Compiled with clang 6:

$ clang++ -std=c++17 main.cpp
main.cpp:6:40: error: constexpr if condition is not a constant expression
    auto f = [](auto& v){ if constexpr(v){} };
                                       ^
main.cpp:8:6: note: in instantiation of function template specialization 
    'main()::(anonymous class)::operator()<const A>' requested here
    f(a);
     ^
1 error generated.

当我将参数复制到局部变量中时,它们都接受代码:

When I copy the parameter into a local variable both accept the code:

struct A{
    constexpr operator bool()const{ return true; }
};

int main(){
    auto f = [](auto v){ auto x = v; if constexpr(x){} };
    A a;
    f(a);
}

我确信第二和第三种情况都将被两个编译器正确处理.我不知道规则是什么.

I am sure that the second and third cases will be handled correctly by both compilers. I don't know what the rule is, though.

在第一种情况下,我怀疑clang是正确的,因为这种情况类似于第二种情况.我想知道在第一种情况下clang或GCC是否正确,在第二种情况下哪种规则使not-constexpr变量v的使用无效,而在第三种情况下的x则有效.

In the first case I suspect that clang is right, because the case resembles the second. I would like to know if in the first case clang or GCC is correct and which rules in the second case makes the use of the not-constexpr variable v invalid and in the third case x valid.

第一个问题现在很清楚: https://gcc.gnu.org/bugzilla/show_bug.cgi?id= 84421

Edit 2: First Question is clear now: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84421

c语是正确的,GCC 7也接受了该代码.该错误将在GCC 8的最终版本中修复.

clang was right, GCC 7 accepted the code as well. The bug will be fixed in the final version of GCC 8.

推荐答案

在所有情况下,Clang都是正确的. [完全公开:我是Clang开发人员]

Clang is correct in all cases. [Full disclosure: I'm a Clang developer]

在所有情况下,问题都可以归结为:我们可以在常量表达式内的v上调用constexpr成员函数吗?

The question in all cases reduces to this: can we call a constexpr member function on v within a constant expression?

要回答此问题,我们需要查看[expr.const] p2,它表示:

To answer this question, we need to look at [expr.const]p2, which says:

表达式e是核心常量表达式,除非按照抽象机器(6.8.1)的规则对e的求值将对以下表达式之一求值:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.8.1), would evaluate one of the following expressions:

  • ...
  • id-expression 引用引用类型的变量或数据成员,除非引用具有 初始化之前,或者
    • 使用常量表达式或
    • 进行初始化
    • 其寿命始于对e的评估;
    • ...
    • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
      • it is initialized with a constant expression or
      • its lifetime began within the evaluation of e;

      其他任何规则均禁止您的任何示例.特别是, 如果不是引用类型,则可以使用常量表达式命名局部变量. (不允许 对它们执行左值到右值转换-即读取它们的值-除非已知它们的值(例如,因为它们是constexpr),并且您最终不能引用此类变量的地址,但您可以 为其命名.)

      None of the other rules prohibit any of your examples. In particular, you are allowed to name local variables in a constant expression if they are not of reference type. (You are not allowed to perform lvalue-to-rvalue conversions on them -- that is, read their values -- unless their value is known (for instance, because they're constexpr), and you're not allowed to end up referring to the address of such a variable, but you are allowed to name them.)

      引用类型的实体规则不同的原因是,即使您对结果不做任何操作,仅命名引用类型的实体也会导致立即解析引用,并且解析引用需要了解它要绑定到什么.

      The reason that the rules are different for entities of reference type is that merely naming an entity of reference type causes the reference to be immediately resolved, even if you don't do anything with the result, and resolving a reference requires knowing what it's bound to.

      所以:第一个例子是有效的. constexpr成员函数的*this值绑定到局部变量a.我们不知道是什么对象,这无关紧要,因为评估无关紧要.

      So: the first example is valid. The *this value of the constexpr member function is bound to the local variable a. It doesn't matter that we don't know what object that is, because the evaluation doesn't care.

      第二个示例(其中v是引用类型)格式错误.仅仅为v命名需要将其解析为它所绑定的对象,这不能作为常量表达式求值的一部分来完成,因为我们不知道它将最终绑定到什么对象.以后的评估步骤不会使用结果对象也没关系;引用被命名后,将立即得到解决.

      The second example (where v is of reference type) is ill-formed. Merely naming v requires resolving it to the object it's bound to, which can't be done as part of the constant expression evaluation because we have no idea what it'll end up being bound to. It doesn't matter that the later evaluation steps won't use the resulting object; references are resolved immediately when they're named.

      第三个示例由于与第一个示例相同的原因而有效.值得注意的是,即使将v更改为引用类型,第三个示例仍然有效:

      The third example is valid for the same reason as the first. Notably, the third example remains valid even if you change v to be of reference type:

      auto f = [](auto &v) { auto x = v; if constexpr (x) {} };
      A a;
      f(a);
      

      ...,因为x再次是我们可以在常量表达式中命名的局部变量.

      ... because x is, once again, a local variable that we can name within a constant expression.

      这篇关于if constexpr中的not-constexpr变量– lang与GCC的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆