What C++14 rule prohibits constexpr functions from making assignments to data members?

三世轮回 提交于 2019-12-10 14:12:12

问题


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

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