C++ constexpr自动成员函数。Clang问题?

5
 #include <utility>

 struct A {
     constexpr auto one(int a) {
         return std::integral_constant<int, _data[a]>{};
     }
     constexpr int  two(int a) const {
         return _data[a];
     }

     int _data[10];
 };

 int main() {
     constexpr auto ex = A{{1,2,3,4,5,6,7,8,9,10}};

     std::integral_constant<int, ex.two(3)> b{};
 }

以上代码在最新版本的Clang编译器中无法通过编译。错误出现在one()成员函数中,错误信息如下:

cc.cpp:57:44: note: implicit use of 'this' pointer is only allowed 
  within the evaluation of a call to a 'constexpr' member function.

显然,该函数被标记为constexpr,如果您注释掉one()成员,一切都能顺利编译。因此,我们显然可以从ex创建integral_constant,但是不能直接从struct中创建吗?当我需要auto返回类型推导时,它似乎失败并声称该函数不是constexpr?这是否合理?我觉得这不应该是问题,如果这是预期的行为,我会感到惊讶。

2
_data[a] 不是 A::one 内的常量表达式。请注意,constexpr 函数仍然可以在运行时调用,例如,如果您提供的参数只在运行时才知道。 - dyp
2
据我所知,在这两个成员函数中,_data[a] 都不是常量表达式。但是,constexpr 函数可以由非常量表达式组成。规则是:在需要常量表达式的上下文中,不能评估非常量表达式(包括 constexpr 函数的部分)。此外,必须有一种方式可以在需要常量表达式的上下文中合法地调用 constexpr 函数。 - dyp
@dyp 你忽略了constexpr是一个限定符,其行为取决于调用站点。因此,two知道Aconstexpr,并且dothis->data[a]。有趣的部分是为什么它认为one是不合法的。 - polkovnikov.ph
1
@polkovnikov.ph 的 two 函数不知道该实例已被声明为 constexpr。请考虑使用 A a {rand()}; a.two(0)。这是完全合法的,但函数调用表达式 a.two(0) 不能成为常量表达式。 - dyp
1
@polkovnikov.ph 注意,我只是使用了 a.two(0),但你正在一个需要常量表达式的上下文中使用它。正如我所说:在这个例子中,a.two(0) 是合法的,但它本身不是一个常量表达式,也不能在常量表达式中求值。 - dyp
显示剩余2条评论
2个回答

7
如果您考虑[dcl.constexpr]/7中的这个语句:
“调用constexpr函数产生与调用等效的非constexpr函数相同的结果,除了constexpr函数的调用可以出现在常量表达式中。”
考虑非constexpr等效函数A :: one()。_data [a]不能在常量表达式中使用(作为非类型模板参数),因为它涉及对this的评估(来自[expr.const]):
条件表达式e是一个核心常量表达式,除非按照抽象机器(1.9)的规则,其评估将评估以下表达式之一:
(2.1) - this(5.1.1),除了在评估为e的constexpr函数或constexpr构造函数中;
由于非constexpr等效函数是不符合规范的,因此constexpr函数给出相同的结果是合理的。
另一方面,two()是一个良好定义的成员函数,无论constexpr如何,您对ex.two(3)的使用都有效作为常量表达式-这就是为什么它编译的原因。

非常棒的答案。您能引用一段标准中的内容,说明为什么two可以在常量表达式中使用吗?当在constexpr对象上调用方法时,this被确定为constexpr限定符是我通过实验方式得出的结论。 - polkovnikov.ph
@polkovnikov.ph 标准提供了一份防止表达式成为常量表达式的事项清单 - 只是其中没有任何一个适用。 [ ] this 除了constexpr 中,[ ] 调用非 constexpr 函数,[ ] 调用未定义函数,[ ] UB,[ ] lambda,[x] lvalue-to-rvalue 转换但 [x] 除非 它引用了使用 constexpr 定义的对象的不可变子对象,... - Barry

2
  1. constexpr函数被设计成可以在编译时和运行时都能调用。
  2. 如果你可以省略constexpr并获得一个适当的普通函数,那么带有constexpr函数的代码是完全正确的。换句话说,它必须作为运行时函数进行编译。
  3. 如果constexpr函数的主体无法在编译时计算,则仍将其编译,但您无法在编译时上下文(例如模板参数)中使用它。
  4. 如果对constexpr对象调用constexpr方法,则认为thisconstexpr

对于one,它是不合法的,因为当它编译为运行时运行时,_data[a]被视为运行时表达式,因为a不是常量表达式,即使thisthis->_data是。

对于two,它可以正常编译,因为在运行时可以正常工作,并且在编译时thisa一样是constexpr的,因此this->_data[a]constexpr,一切正常。


移除 one 函数编译正常,而 two 在编译时间上下文中被使用? - pat
字面上来说,在这个例子中,我使用调用两个作为模板参数。 - pat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接