constexpr成员函数不使用此功能?

问题描述 投票:9回答:3

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

这两个程序都没有,还是两个都不正确?

为什么/为什么不呢?

c++ c++14 language-lawyer constexpr
3个回答
6
投票

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

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

constexpr int f(int) { return 42; }

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

但是,其格式不正确的原因是由于非恒定表达式的左值到右值转换,这是表达式的evaluation不允许执行的操作之一。在调用s.f()的情况下,不会发生左值到右值的转换。


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变量,但是,如果未实际使用此路径,则不会出错。

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


-3
投票

这听起来像是一个测验问题,不是由学生提出的,而是教授在stackoverflow上对公众进行测试,但让我们看看...

让我们从一个定义规则开始。很明显,两个版本都没有违反该版本,因此它们都通过了该部分。

然后是语法。两者都没有语法错误,如果您不介意语法和语义问题的潜在结合,它们都将毫无问题地进行编译。

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

[现在,可能有歧义的return g(S());显然外部g是函数调用,但是S可能不像使用{}初始化S编写的return g(S{});那样清晰,将来应该没有歧义。 struct S用operator()扩展(该结构已经几乎类似于仿函数了)。调用的构造函数现在会自动生成,并且在此版本中没有operator()会给编译器造成混乱,但是现代C ++ 14应该提供更清晰的选择,以避免出现“最复杂的解析”,g(S ())类似。

所以,我不得不说,基于语义规则,它们都失败了(虽然不是很严重)。

© www.soinside.com 2019 - 2024. All rights reserved.