CWG Issue 1581 (original) (raw)

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

2025-11-05


1581. When are constexpr member functions defined?

Section: 6.3 [basic.def.odr]Status: CD5Submitter: Richard SmithDate: 2012-10-29

[Accepted as a DR as paper P0859R0 at the October, 2017 meeting.]

11.4.4 [special] is perfectly clear that special member functions are only implicitly defined when they are odr-used. This creates a problem for constant expressions in unevaluated contexts:

struct duration { constexpr duration() {} constexpr operator int() const { return 0; } }; // duration d = duration(); // #1 int n = sizeof(short{duration(duration())});

The issue here is that we are not permitted to implicitly defineconstexpr duration::duration(duration&&) in this program, so the expression in the initializer list is not a constant expression (because it invokes a constexpr function which has not been defined), so the braced initializer contains a narrowing conversion, so the program is ill-formed.

If we uncomment line #1, the move constructor is implicitly defined and the program is valid. This spooky action at a distance is extremely unfortunate. Implementations diverge on this point.

There are also similar problems with implicit instantiation ofconstexpr functions. It is not clear which contexts require their instantiation. For example:

template struct U {}; int g(int); template constexpr int h(T) { return T::error; } template auto f(T t) -> U<g(T()) + h(T())> {} int f(...); int k = f(0);

There are at least two different ways of modeling the current rules:

These two approaches can be distinguished by code like this:

int k = sizeof(U<0 && h(0)>);

Under the first approach, this code is valid; under the second, it is ill-formed.

A possible approach to resolving this issue would be to change the definition of “potentially-evaluated” such that template arguments, array bounds, and braced-init-lists (and any other expressions which are constant evaluated) are always potentially-evaluated, even if they appear within an unevaluated context, and to change 13.9.2 [temp.inst] paragraph 3 to say simply that function template specializations are implicitly instantiated when they are odr-used.

A related question is whether putatively constexprconstructors must be instantiated in order to determine whether their class is a literal type or not. Seeissue 1358.

Jason Merrill:

I'm concerned about unintended side-effects of such a large change to “potentially-evaluated;” I would prefer something that only affects constexpr.

It occurs to me that this is parallel to issue 1330: just like we want to instantiate exception specifiers for calls in unevaluated context, we also want to instantiateconstexpr functions. I think we should define some other term to say when there's a reference to a declaration, and then say that the declaration is odr-used when that happens in potentially-evaluated context.

Notes from the April, 2013 meeting:

An additional question was raised about whether constexprfunctions should be instantiated as a result of appearing within unevaluated subexpressions of constant expressions. For example:

#include

template constexpr T f(T t) { return +t; }

struct A { };

template decltype(std::is_scalar::value ? T::fail : f(T())) g() { }

template void g(...);

int main() { g(); }

If constexpr instantiation happens during constant expression evaluation, f is never instantiated and the program is well-formed. If constexpr instantiation happens during parsing,f is instantiated and the program is ill-formed.