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拒绝“守则”,并:
$ 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){} };
^谁是正确的,为什么?
当我在每个引用中接受参数时,两种方法都拒绝代码:
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto& v){ if constexpr(v){} };
constexpr A a;
f(a);
}用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.当我将参数复制到局部变量中时,双方都接受以下代码:
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto v){ auto x = v; if constexpr(x){} };
A a;
f(a);
}编辑:我确信第二和第三种情况将由两个编译器正确处理。不过,我不知道规矩是什么。
在第一种情况下,我怀疑clang是对的,因为这种情况类似于第二种情况。我想知道,在第一种情况下,clang或GCC是否是正确的,在第二种情况下,哪些规则使非恒定变量v无效,而在第三种情况下,x是有效的。
编辑2:第一个问题现在很清楚:bug.cgi?id=84421
clang是对的,GCC 7也接受了这个代码。这个错误将在GCC 8的最终版本中修复。
发布于 2018-02-17 01:19:22
Clang在任何情况下都是正确的。完全披露:我是一个Clang开发人员
在所有情况下,问题都归结为:我们可以在常量表达式中调用v上的v成员函数吗?
要回答这个问题,我们需要看看expr.constp2,它说:
表达式e是一个核心常量表达式,除非e的计算遵循抽象机器(6.8.1)的规则计算下列表达式之一:
e评价范围内开始;
其他任何规则都不禁止你举任何例子。特别是,如果局部变量不是引用类型,则允许在常量表达式中命名它们。(不允许对它们执行lvalue到rvalue的转换-也就是说,读取它们的值--除非它们的值已知(例如,因为它们是constexpr),并且不允许您最终引用这样一个变量的地址,但是您可以命名它们。)
对于引用类型的实体,规则不同的原因是,仅仅命名引用类型的实体就会立即解析引用,即使您没有对结果做任何事情,解析引用也需要知道它绑定到什么。
所以:第一个例子是有效的。*this成员函数的constexpr值绑定到局部变量a。我们不知道那是什么对象并不重要,因为评估并不在乎。
第二个示例(其中v是引用类型的)格式不正确。仅仅命名v需要将其解析到它绑定到的对象,这不能作为常量表达式计算的一部分来完成,因为我们不知道它最终会被绑定到什么。重要的是,后面的评估步骤不会使用结果对象;引用在命名时立即被解析。
第三个示例的有效原因与第一个示例相同。值得注意的是,即使您将v更改为引用类型,第三个示例仍然有效:
auto f = [](auto &v) { auto x = v; if constexpr (x) {} };
A a;
f(a);..。因为x再次是一个局部变量,我们可以在常量表达式中命名它。
https://stackoverflow.com/questions/48826555
复制相似问题