This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Open status.

### 2833. Library needs to specify what it means when it declares a function `constexpr`

Section: 22.6.3.2 [variant.ctor] Status: Open Submitter: Richard Smith Opened: 2016-11-28 Last modified: 2020-12-19

Priority: 2

View other active issues in [variant.ctor].

View all other issues in [variant.ctor].

View all issues with Open status.

Discussion:

The library has lots of functions declared `constexpr`, but it's not clear what that means. The `constexpr` keyword implies that there needs to be some invocation of the function, for some set of template arguments and function arguments, that is valid in a constant expression (otherwise the program would be ill-formed, with no diagnostic required), along with a few side conditions. I suspect the library intends to require something a lot stronger than that from implementations (something along the lines of "all calls that could reasonably be constant subexpressions are in fact constant subexpressions, unless otherwise stated").

[variant.ctor]/1 contains this, which should also be fixed:

> "This function shall be `constexpr` if and only if the value-initialization of the alternative type `T0` would satisfy the requirements for a `constexpr` function."

This is the wrong constraint: instead of constraining whether the function is `constexpr`, we should constrain whether a call to it is a constant subexpression.

Daniel:

This is has some considerable overlap with LWG 2289 but is phrased in a more general way.

[2016-12-16, Issues Telecon]

Priority 2; this is also the general case of 2829.

[2017-02-20, Alisdair comments and suggests concrete wording]

Below is is draft wording I was working on at Issaquah to try to address both issues.

[2017-11 Albuquerque Wednesday issue processing]

Status to Open; really needs a paper.

STL says "What about `plus<T>`?" `plus<int>` needs to be usable in a constexpr context, but `plus<string>` can't be.

[2017-11 Albuquerque Saturday issues processing]

Geoffrey to write a paper resolving this.

[2018-06 Rapperswil Thursday issues processing]

Geoffrey has been unable to write this paper due to time constraints. He wrote up his progress here. Daniel has offered to help someone to write this paper; he's willing to be a co-author.

[2018-08-23 Batavia Issues processing]

Michael Wong to investigate.

Previous resolution from Daniel [SUPERSEDED]:

> This wording is relative to N4640.

> 1. Modify 16.4.6.7 [constexpr.functions] as indicated:

>    > ### 17.6.5.6 `constexpr` functions and constructors [constexpr.functions]

>    > -1- This International Standard explicitly requires that certain standard library functions are `constexpr` (9.2.6 [dcl.constexpr]). If the specification for a templated entity requires that it shall be a `constexpr` templated entity, then that templated entity shall be usable in a constant expression.. An implementation shall not may declare any additional standard library function signature as `constexpr` except for those where it is explicitly required. Within any header that provides any non-defining declarations of `constexpr` functions or constructors an implementation shall provide corresponding definitions.

[2020-06-08 Nina Dinka Ranns comments and provides alternative wording]

The revised wording draft also resolves LWG 2289, LWG 2829, and LWG 3215.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4861.

> 1. 1. Modify 16.4.6.7 [constexpr.functions] as indicated:

>    > -1- This document explicitly requires that certain standard library functions are `constexpr` (9.2.6 [dcl.constexpr]). An implementation shall not declare any standard library function signature as `constexpr` except for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.

>    > -?- Let `F` denote a standard library function template or member function of a class template. If the specification of `F` declares it to be `constexpr`, unless otherwise specified, then `F` can be used in a constant expression if and only if all the expressions that are evaluated as specified in the description of `F`'s semantics can be used in a constant expression.

> 2. 2. - 10. […] // Remainder of Nina's update

[2020-10-02 Jens Maurer improves wording]

Specifically the wording for 16.4.6.7 [constexpr.functions] needs improvement and is updated below.

[2020-10-02 Tim Song comments]

The new wording doesn't cover the following example:

>     // global scope
>     int x;
>     int y;

>     constexpr int j = (std::swap(x, y), 0); // error

- `swap` is a "standard library function template...declared `constexpr`"

- `x` and `y` are (lvalue) constant expressions

- `std::swap(x, y)` is plainly not a constant expression

[2020-10-04 Jens Maurer comments]

Yes, we're still lacking text for that (and maybe Nina's old text helps for that).

[2020-12-14; Jiang An comments]

The item "constexpr functions" is also used in 24.2.2.1 [container.requirements.general]/14 and 25.3.1 [iterator.requirements.general]/16, and such usage should also be modified by this issue here.

Proposed resolution:

This wording is relative to N4861.

1. Modify 16.4.6.7 [constexpr.functions] as indicated:

   > -1- This document explicitly requires that certain standard library functions are `constexpr` (9.2.6 [dcl.constexpr]). An implementation shall not declare any standard library function signature as `constexpr` except for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.

   > -?- Let `F` denote a standard library function template or member function of a class template declared `constexpr`. Unless otherwise specified, a function call expression (7.6.1.3 [expr.call]) whose postfix-expression names `F` is a constant expression if all of the argument subexpressions are constant expressions.

2. Modify 22.3.2 [pairs.pair] as indicated:

   > -2- The defaulted move and copy constructors, respectively, of `pair` is a constexpr function can be used in a constant expression if and only if all required element-wise initializations for move and copy, respectively, would satisfy the requirements for a constexpr function can be used in a constant expression.

3. Modify 22.4.4.1 [tuple.cnstr] as indicated:

   > -3- The defaulted move and copy constructors, respectively, of `tuple` is a constexpr function can be used in a constant expression if and only if all required element-wise initializations for move and copy, respectively, would satisfy the requirements for a constexpr function can be used in a constant expression. The defaulted move and copy constructors of `tuple<>` are constexpr functions can be used in a constant expression.

4. Modify 22.5.3.2 [optional.ctor] as indicated:

   >     constexpr optional() noexcept;
   >     constexpr optional(nullopt_t) noexcept;

   > > -1- […]

   > > -2- Remarks: No contained value is initialized. For every object type `T` these constructors are constexpr constructors (9.2.6 [dcl.constexpr]).

   > […]

   >     template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);

   > > -12- […]

   > > -13- […]

   > > -14- […]

   > > -15- […]

   > > -16- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class U, class... Args>
   >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

   > > -17- […]

   > > -18- […]

   > > -19- […]

   > > -20- […]

   > > -21- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class U = T> constexpr explicit(see below) optional(U&& v);

   > > -22- […]

   > > -23- […]

   > > -24- […]

   > > -25- […]

   > > -26- Remarks: If `T`'s constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor. The expression inside `explicit` is equivalent to:

   > > >     !is_convertible_v<U, T>

5. Modify 22.5.3.6 [optional.observe] as indicated:

   >     constexpr const T* operator->() const;
   >     constexpr T* operator->();

   > > -1- […]

   > > -2- […]

   > > -3- […]

   > > -4- Remarks: These functions are constexpr functions.

   >     constexpr const T& operator*() const&;
   >     constexpr T& operator*() &;

   > > -5- […]

   > > -6- […]

   > > -7- […]

   > > -8- Remarks: These functions are constexpr functions.

   > […]

   >     constexpr explicit operator bool() const noexcept;

   > > -11- Returns: `true` if and only if `*this` contains a value.

   > > -12- Remarks: This function is a constexpr function.

   >     constexpr bool has_value() const noexcept;

   > > -13- Returns: `true` if and only if `*this` contains a value.

   > > -14- Remarks: This function is a constexpr function.

6. Modify 22.5.6 [optional.relops] as indicated:

   >     template<class T, class U> constexpr bool operator==(const optional<T>& x, const optional<U>& y);

   > > -1- […]

   > > -2- […]

   > > -3- Remarks: Specializations of this function template for which `*x == *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator!=(const optional<T>& x, const optional<U>& y);

   > > -4- […]

   > > -5- […]

   > > -6- Remarks: Specializations of this function template for which `*x != *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator<(const optional<T>& x, const optional<U>& y);

   > > -7- […]

   > > -8- […]

   > > -9- Remarks: Specializations of this function template for which `*x < *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator>(const optional<T>& x, const optional<U>& y);

   > > -10- […]

   > > -11- […]

   > > -12- Remarks: Specializations of this function template for which `*x > *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator<=(const optional<T>& x, const optional<U>& y);

   > > -13- […]

   > > -14- […]

   > > -15- Remarks: Specializations of this function template for which `*x <= *y` is a core constant expression are constexpr functions.

   >     template<class T, class U> constexpr bool operator>=(const optional<T>& x, const optional<U>& y);

   > > -16- […]

   > > -17- […]

   > > -18- Remarks: Specializations of this function template for which `*x >= *y` is a core constant expression are constexpr functions.

   >     template<class T, three_way_comparable_with<T> U>
   >       constexpr compare_three_way_result_t<T,U>
   >         operator<=>(const optional<T>& x, const optional<U>& y);

   > > -19- Returns: If `x && y`, `*x <=> *y`; otherwise `bool(x) <=> bool(y)`.

   > > -20- Remarks: Specializations of this function template for which `*x <=> *y` is a core constant expression are constexpr functions.

7. Modify 22.6.3.2 [variant.ctor] as indicated:

   >     constexpr variant() noexcept(see below);

   > > -1- […]

   > > -2- […]

   > > -3- […]

   > > -4- […]

   > > -5- […]

   > > -6- Remarks: This function is `constexpr` if and only if the value-initialization of the alternative type `T0` would satisfy the requirements for a constexpr function. The expression inside `noexcept` is equivalent to `is_nothrow_default_constructible_v<T0>`. [Note: See also class `monostate`. — end note]

   > […]

   >     template<class T> constexpr variant(T&& t) noexcept(see below);

   > > -14- […]

   > > […]

   > > -19- Remarks: The expression inside `noexcept` is equivalent to `is_nothrow_constructible_v<Tj, T>`. If `Tj`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);

   > > -20- […]

   > > […]

   > > -24- Remarks: If `T`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<class T, class U, class... Args>
   >       constexpr explicit variant(in_place_type_t<T>, initializer_list<U> il, Args&&... args);

   > > -25- […]

   > > […]

   > > -29- Remarks: If `T`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);

   > > -30- […]

   > > […]

   > > -34- Remarks: If `TI`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

   >     template<size_t I, class U, class... Args>
   >       constexpr explicit variant(in_place_index_t<I>, initializer_list<U> il, Args&&... args);

   > > -35- […]

   > > […]

   > > -38- Remarks: If `TI`'s selected constructor is a constexpr constructor, this constructor is a constexpr constructor.

8. Modify 25.5.4.11 [move.sent.ops] as indicated:

   >     constexpr move_sentinel();

   > > -1- Effects: Value-initializes `last`. If `is_trivially_default_constructible_v<S>` is `true`, then this constructor is a constexpr constructor.

9. Modify 22.15.3 [bit.cast] as indicated:

   >     template<class To, class From>
   >       constexpr To bit_cast(const From& from) noexcept;

   > > -1- […]

   > > -3- Remarks: This function is `constexpr` can be used in a constant expression if and only if `To`, `From`, and the types of all subobjects of `To` and From are types `T` such that:

   > > 1. (3.1) — `is_union_v<T>` is `false`;

   > > 2. (3.2) — `is_pointer_v<T>` is `false`;

   > > 3. (3.3) — `is_member_pointer_v<T>` is `false`;

   > > 4. (3.4) — `is_volatile_v<T>` is `false`; and

   > > 5. (3.5) — `T` has no non-static data members of reference type.

10. Modify 29.5 [time.duration] as indicated:

    > -5- The defaulted copy constructors of duration shall be a constexpr function can be used in a constant expression if and only if the required initialization of the member `rep_` for copy and move, respectively, would satisfy the requirements for a constexpr function can be used in a constant expression.