Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr member functions that don't use this?

Please consider the following two C++14 programs:

Program 1:

struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }

Program 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); }

Are neither, either or both of these programs ill-formed?

Why/why not?

like image 577
Andrew Tomazos Avatar asked Sep 27 '15 01:09

Andrew Tomazos


2 Answers

Both programs are well-formed. The C++14 standard requires that s.f() be a constant expression because it is being used to initialize a constexpr variable, and in fact it is a core constant expression because there's no reason for it not to be. The reasons that an expression might not be a core constant expression are listed in section 5.19 p2. In particular, it states that the evaluation of the expression would have to do one of several things, none of which are done in your examples.

This may be surprising since, in some contexts, passing a non-constant expression to a constexpr function can cause the result to be a non-constant expression even if the argument isn't used. For example:

constexpr int f(int) { return 42; }

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

However, the reason this is ill-formed is because of the lvalue-to-rvalue conversion of a non-constant expression, which is one of the things that the evaluation of the expression is not allowed to do. An lvalue-to-rvalue conversion doesn't occur in the case of calling s.f().

like image 65
Vaughn Cato Avatar answered Oct 18 '22 05:10

Vaughn Cato


I can't seem to find a compelling passage or example in the standard that directly addresses the issue of calling a constexpr member function on a non-constexpr instance, but here are some that may be of help (from draft 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

From this I take that the program is not outright ill-formed just because a constexpr function has a possible non-constexpr path.

[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

This is somewhat closer to your example programs, here a constexpr constructor may reference a non-constexpr variable depending on the value of the argument, but there is no error if this path is not actually taken.

So I don't think either program you presented should be ill-formed, but I cannot offer convincing proof :)

like image 27
melak47 Avatar answered Oct 18 '22 07:10

melak47