问题
My understanding is that this (nonsensical) code is not valid C++14:
class Point {
public:
constexpr double setX(double newX) { return x = newX; }
private:
double x;
};
I'm trying to figure out what part of the (still officially draft) C++14 Standard disallows it. The restrictions on constexpr functions are listed in 7.1.5/2. (Sorry for the mangled formatting. I can't figure out how to beat markdown into making it look right.)
The definition of a constexpr function shall satisfy the following constraints:
- it shall not be virtual (10.3);
- its return type shall be a literal type;
- each of its parameter types shall be a literal type;
- its function-body shall be = delete, = default, or a compound-statement that does not contain
- an asm-definition,
- a goto statement,
- a try-block, or
- a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.
There's nothing there that prohibits assignments to data members. There is such a prohibition in 5.19/2 (bullet 15) (again with mangled formatting, sorry):
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: [...] modification of an object (5.17, 5.2.6, 5.3.2) unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;
But I don't see how 5.19 applies to 7.1.5. Can somebody clarify?
回答1:
It is valid C++14. You can modify members of a literal class type as long as the lifetime of the object is contained within the evaluation of the constant expression.
The use of Point
in a constant expression is controversial (CWG DR 1452), but it is allowed by current implementations. It would be a literal class except that it is not aggregate (§3.9.1/10) because it has a private field (§8.5.1/1). However its construction does not invoke its non-constexpr constructor because it is trivially-constructible. Anyway, this issue is fixed by adding a declaration constexpr Point() = default;
.
§5.19 restricts what can be evaluated in a constant expression. One restriction is that only constexpr
functions may be entered. §7.1.5 specifies what functions may be marked constexpr
, but note that constexpr
functions may contain (in a conditional statement) things that cannot be evaluated in a constant expression.
See the proposal papers, second and first drafts.
回答2:
If you add an in-class initializer to x
, e.g. {}
, then it compiles on Clang 3.4 in -std=c++1y
mode:
class Point {
public:
constexpr double setX(double newX) { return x = newX; }
constexpr double getX() const { return x; }
private:
double x {}; // without init: "error: constexpr function never produces a constant expression [-Winvalid-constexpr]"
};
constexpr Point f()
{
Point p;
p.setX(1.0);
return p;
}
int main()
{
auto constexpr p = f();
static_assert(p.getX() == 1.0, "");
}
Live Example.
If you have other constructors for Point
, you need to add constexpr Point() = default;
in order for it to work.
来源:https://stackoverflow.com/questions/25152301/what-c14-rule-prohibits-constexpr-functions-from-making-assignments-to-data-me