This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-10-26


2022. Copy elision in constant expressions

Section: 7.7  [expr.const]     Status: CD4     Submitter: Jason Merrill     Date: 2014-10-16

[Adopted at the June, 2016 meeting.]

Consider the following example:

  struct A {
    void *p;
    constexpr A(): p(this) {}
  };

  constexpr A a;		// well-formed
  constexpr A b = A();          // ?

The declaration of a seems well-formed because the address of a is constant.

The declaration of b, however, seems to depend on whether copy elision is performed. If it is, the declaration is the equivalent of a; if not, however, this creates a temporary and initializes p to the address of that temporary, making the initialization non-constant and the declaration ill-formed.

It does not seem desirable for the well-formedness of the program to depend on whether the implementation performs an optional copy elision.

Notes from the November, 2014 meeting:

CWG decided to leave it unspecified whether copy elision is performed in cases like this and to add a note to 11.4.5.3 [class.copy.ctor] to make clear that that outcome is intentional.

Notes from the May, 2015 meeting:

CWG agreed that copy elision should be mandatory in constant expressions.

Proposed resolution (April, 2016):

  1. Change 7.7 [expr.const] paragraph 1 as follows:

  2. ...Expressions that satisfy these requirements, assuming that copy elision is performed, are called constant expressions. [Note:...
  3. Change 9.2.6 [dcl.constexpr] paragraph 7 as follows, breaking the existing running text into a bulleted list:

  4. A call to a constexpr function produces the same result as a call to an equivalent non-constexpr function in all respects except that

  5. Change 11.4.5.3 [class.copy.ctor] paragraph 31 as follows:

  6. ...This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

    Copy elision is required where an expression is evaluated in a context requiring a constant expression (7.7 [expr.const]) and in constant initialization (6.9.3.2 [basic.start.static]). [Note: Copy elision might not be performed if the same expression is evaluated in another context. —end note] [Example:

      class Thing {
      public:
        Thing();
        ~Thing();
        Thing(const Thing&);
      };
    
      Thing f() {
        Thing t;
        return t;
      }
    
      Thing t2 = f();
    
      struct A {
        void *p;
        constexpr A(): p(this) {}
      };
    
      constexpr A a;        // well-formed, a.p points to a
      constexpr A b = A();  // well-formed, b.p points to b
    
      void g() {
        A c = A();          // well-formed, c.p may point to c or to an ephemeral temporary
      }
    

    Here the criteria for elision can be combined...