constexpr member functions that don't use this?

◇◆丶佛笑我妖孽 提交于 2019-12-08 15:59:12

问题


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?


回答1:


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().




回答2:


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 :)




回答3:


This sounds like a quiz question, and not presented by a student, but the professor testing the public on stackoverflow, but let's see...

Let's start with the One Definition Rule. It's clear neither version violates that, so they both pass that part.

Then, to syntax. Neither have syntax failures, they'll both compile without issue if you don't mind the potential blend of a syntax and semantic issue.

First, the simpler semantic issue. This isn't a syntax problem, but f(), in both versions, is the member of a struct, and the function clearly makes no change to the owning struct, it's returning a constant. Although the function is declared constexpr, it is not declared as const, which means if there were some reason to call this as a runtime function, it would generate an error if that attempt were made on a const S. That affects both versions.

Now, the potentially ambiguous return g(S()); Clearly the outer g is a function call, but S may not be so clear as it would be if written return g(S{}); With {} initializing S, there would be no ambiguity in the future should struct S be expanded with an operator() (the struct nearly resembles a functor already). The constructor invoked is automatically generated now, and there is no operator() to create confusion for the compiler at this version, but modern C++14 is supposed to offer clearer alternatives to avoid the "Most Vexing Parse", which g(S()) resembles.

So, I'd have to say that based on semantic rules, they both fail (not so badly though).



来源:https://stackoverflow.com/questions/32803708/constexpr-member-functions-that-dont-use-this

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!