请考虑以下两个C ++ 14程序:
计划1:
struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
计划2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
这些程序中的任何一个或两个都不成形吗?
为什么/为什么不呢?
答案 0 :(得分:6)
这两个项目都是格式良好的。 C ++ 14标准要求s.f()
是一个常量表达式,因为它用于初始化constexpr
变量,实际上它是一个核心常量表达式,因为它没有理由它不是。表达式可能不是核心常量表达式的原因在第5.19节p2中列出。特别是,它声明表达式的评估必须执行以下几项操作中的一项,但没有一项在您的示例中完成。
这可能会令人惊讶,因为在某些情况下,将非常量表达式传递给constexpr
函数会导致结果为非常量表达式,即使参数未被使用也是如此。例如:
constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr y = f(x); // ill-formed
}
然而,这是不正确的原因是因为非常量表达式的左值到右值转换,这是表达式的评估之一允许这样做。在调用s.f()
的情况下,不会发生左值到右值的转换。
答案 1 :(得分:1)
我似乎无法在标准中找到一个引人注目的段落或示例,它直接解决了在非constexpr实例上调用constexpr成员函数的问题,但这里有一些可能有帮助的(来自草案) N4140):
[C++14: 7.1.5/5]
:
对于非模板,非默认
constexpr
函数或非模板,非默认,非继承constexpr
构造函数,如果不存在参数值,则调用函数或构造函数 可能是核心常量表达式的评估子表达式(5.19),该程序是不正确的;没有 需要诊断。
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
由此可见,因为constexpr函数有一个非constexpr路径,所以程序并不是完全错误的。
[C++14: 5.19]
:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
这有点接近你的示例程序,这里一个constexpr构造函数可能会根据参数的值引用一个非constexpr变量,但如果没有实际采用这个路径则没有错误。
所以我不认为你提出的任何一个项目都应该是格式错误的,但我无法提供令人信服的证明:)
答案 2 :(得分:-3)
这听起来像是一个测验问题,并不是由学生提出,而是教授在stackoverflow上测试公众,但让我们看看......
让我们从一个定义规则开始。很明显,两个版本都没有违反,所以他们都通过了那个部分。
然后,语法。如果你不介意语法和语义问题的潜在混合,它们都没有语法失败,它们都会毫无问题地编译。
首先,更简单的语义问题。这不是语法问题,但是f()
在两个版本中都是结构的成员,并且该函数显然没有对拥有的结构进行更改,它返回一个常量。虽然函数声明为constexpr,但它不是声明为const,这意味着如果有某种理由将其称为运行时函数,如果在const S上进行该尝试,则会产生错误。这会影响两个版本。< / p>
现在,可能含糊不清的return g(S());
显然外部g是一个函数调用,但是S可能不会像写return g(S{});
时那样清楚{}} {}初始化S,就没有了未来的模糊性应该struct S
使用operator()扩展(结构几乎类似于仿函数)。现在自动生成调用的构造函数,并且没有operator()在此版本中为编译器创建混淆,但是现代C ++ 14应该提供更清晰的替代方法以避免“最令人烦恼的解析”,其中g(S ())类似。
所以,我不得不说基于语义规则,它们都失败了(尽管不是很糟糕)。