不使用这个的 constexpr 成员函数?

constexpr member functions that don't use this?

请考虑以下两个 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); }

这些程序中的一个或两个都不是格式错误的吗?

Why/why不是吗?

这听起来像是一个测验问题,不是学生提出的,而是教授在 Whosebug 上测试 public,但让我们看看...

让我们从一个定义规则开始。很明显两个版本都没有违反这一点,所以他们都通过了那部分。

然后,到了语法。两者都没有语法错误,如果您不介意语法和语义问题的潜在混合,它们都可以毫无问题地编译。

首先,更简单的语义问题。这不是语法问题,但 f() 在两个版本中都是结构的成员,并且该函数显然没有对拥有的结构进行任何更改,它返回一个常量。尽管该函数被声明为 constexpr,但它并未声明为 const,这意味着如果有某种原因将其作为运行时函数调用,那么如果该尝试是在 const S 上进行的,则会产生错误。这会影响两个版本。

现在,潜在的歧义 return g(S()); 很明显外层 g 是一个函数调用,但是 S 可能不像写的那么清楚 return g(S{}); 使用 {} 初始化 S,会有毫无疑问,将来 struct S 应该用 operator() 扩展(该结构已经几乎类似于仿函数)。调用的构造函数现在自动生成,并且没有 operator() 在此版本中为编译器造成混淆,但现代 C++14 应该提供更清晰的替代方案来避免 "Most Vexing Parse",其中 g(S ()) 类似。

所以,我不得不说,基于语义规则,它们都失败了(虽然不是那么糟糕)。

两个程序的格式都正确。 C++14 标准要求 s.f() 是一个常量表达式,因为它被用来初始化一个 constexpr 变量,事实上它是一个核心常量表达式,因为没有理由不这样做.表达式可能不是核心常量表达式的原因在第 5.19 节 p2 中列出。特别是,它指出表达式的 evaluation 必须做几件事之一,none 在您的示例中已完成。

这可能令人惊讶,因为在某些情况下,将非常量表达式传递给 constexpr 函数可能会导致结果成为非常量表达式,即使未使用参数也是如此。例如:

constexpr int f(int) { return 42; }

int main()
{
    int x = 5;
    constexpr auto y = f(x); // ill-formed
}

但是,这是错误格式的原因是非常量表达式的左值到右值的转换,这是 求值 的事情之一的表达是不允许做的。在调用 s.f().

的情况下不会发生左值到右值的转换

我似乎无法在标准中找到直接解决在非 constexpr 实例上调用 constexpr 成员函数的问题的令人信服的段落或示例,但这里有一些可能有帮助(来自草案N4140):

[C++14: 7.1.5/5]:

For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.

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 变量,但如果实际上没有采用此路径,则不会出错。

所以我认为您提供的任何一个程序都不应该是格式错误的,但我无法提供令人信服的证据:)