Repository navigation
CWG3175 [temp.inst] Clarify if initializer is needed when implicitly instantiating a non-static data member that is not used in constexpr context #830
Description
Activity
MSVC, GCC and EDG accept the program while Clang rejects it. Demo. Here is the program:
struct Wrapper { explicit Wrapper(int) {} }; template <int N = 0> struct Test { Test() : w(0) {} Wrapper w{}; //MSVC, GCC, EDG: Ok but Clang rejects }; Test t; int main() { }EDG also rejects in native mode.
I think it makes sense to have "when-needed" treatment for default member initializers.
Reacted by A. JiangThere has been some prior discussion of this topic at https://lists.isocpp.org/core/2025/02/17366.php
A way of looking at my previously expressed views is that, just like a function, a default member initializer has both a declaration and a definition that can be instantiated when needed. Instantiating the enclosing class does neither. Using the default member initializer only from an unevaluated context instantiates the declaration, which has a similar effect to instantiating a decltype-specifier in a function's declared return type. Using the default member initializer from a potentially-evaluated context instantiates the definition, which means that everything referenced by it is odr-used if it would be if the same expression appeared in a function body.
@t3nsor Yes, exactly as also said in the suggested resolution of this issue. I think what we suggested as resolution is also the most expected behavior and would not confuse people. That is, if the default mem-initializer behaves same as the non-static member function in this instantiation context, then that would be avoid any surprise and so would be the expected behavior.
@jensmaurer Can we have a CWG issue for this. Suggested resolution can be what @t3nsor suggested.
@ranaanoop , care to provide actual wording diffs against the current standard to implement this?
Maybe something like this...
Edit [temp.inst]/11, and maybe add a line break:
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class or static data member of a templated class, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.
[Note 5: The instantiation of a generic lambda does not require instantiation of substatements of a constexpr if statement within its compound-statement unless the call operator template is instantiated. — end note]
It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.
The use of a template specialization in a default argument or default member initializer shall not cause the template to be implicitly instantiated except where needed to determine the correctness of the default argument or default member initializer. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated. Similarly, the use of a default member initializer in a constructor definition or an aggregate initialization causes specializations in the default member initializer to be instantiated.Let I be a default argument or default member initializer for a parameter or member, respectively, of type
T, considering I to include the initialization of the parameter or member from the initializer-clause or brace-or-equal-initializer, respectively. Each templated entity X referenced by I is instantiated if needed to determine the correctness of I. Additionally, for the purposes of determining whether a definition of X is required to exist or whether the existence of a definition for X affects the semantics of the program, I is considered to be a templated function F having the formT F() { return T I; }, and a function call, constructor definition or aggregate initialization that uses I is considered to call F. [Note: Whether X is instantiated can therefore depend on whether the definition of F is instantiated, which in turn can depend on whether a function call or aggregate initialization that uses I is potentially-evaluated. This rule applies even if I is not itself templated.]@jensmaurer thoughts?
Although I believe the proposed wording doesn't address the example at the top of this issue, because Wrapper is not templated, so it's not an "X" covered by the wording.
- changed the title
[-][temp.inst] Clarify if initializer is needed when implicitly instantiating a non-static data member that is not used in constexpr context[/-][+]CWG3175 [temp.inst] Clarify if initializer is needed when implicitly instantiating a non-static data member that is not used in constexpr context[/+]on Apr 16, 2026 True, accepting that program would require us to make default member initializers also separately instantiated constructs, which I suppose we could do, but seems a bit evolutionary.
Putting this here in case I forget. In non-template code:
- No implementation requires a definition of a non-consteval function that is referenced from a default member initializer if that default member initializer is used only from unevaluated contexts. This suggests that they do not treat the reference as an odr-use (though, as we all know, ODR violations are not required to be diagnosed).
- Clang and GCC do not require a definition for a
constevalfunction that is called in a default member initializer if that default member initializer is used only from unevaluated contexts.
Full name of submitter: Anoop S. Rana
Reference (section label): temp.inst
Link to reflector thread (if any): https://stackoverflow.com/questions/79844065/is-gcc-right-when-it-accepts-a-class-template-having-a-member-with-a-wrong-defau
Issue description: Consider the following program which shows implementation divergence. MSVC, GCC, EDG accept the program while Clang rejects it.
There is also an old gcc bug that says this is ill-formed. For completeness, I'll describe what I think is the intention and what is given in the current standard. As a summary, I think one intention here is to make this ill-formed because the default ctor won't be implicitly generated for
Wrapperso even writingWrapper w{};shouldn't be possible but since templates allow lazy instantiation and more importantly since the initializer is not supposed to be needed for non constexpr contexts, the other intention may be to allow it. Now, let's see what the current standard says about this:First note that
Wrapper w{};is direct initialisation(direct list initialization to be more specific). From dcl.init:Next we move to the semantics of the initializer.
The above means that, the object is to be list-initialized.
From list initialization:
The above means that
Wrapper w{};is direct-list initiaization. Next we see the effect of list initialization:The above means that the object will be value initialized, so we move on to value-initialization:
This means that the object will be default initialized:
So we move on to over.match.ctor to get a list of candidate ctors. Also do note the
initializer()part in the above quoted reference as it will be used at the end as an argument.But note that there is no default constructor for
Wrapper. As you've provided your own parameterized ctor, the compiler won't synthesize a default ctor forWrapper. Thus, the condition forWrapper w{};to be valid(which requires a default ctor) is not fulfilled and clang seems to be correct in rejecting the program.But note that we also have temp.inst#3:
Note that this does not say anything about default member initializer of non-static data members, so it isn't clear(I think) if
Test t;results in use of the initializer.Suggested Resolution: I think this can be solved by specifying that only declaration of the non-static data member is instantiated(just like declaration of non-static member function) and so initializer is not used here and the program is intended to be well-formed. Note that this would also make this program also well-formed.