Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my class non default-constructible?

Tags:

c++

nsdmi

I have those classes:

#include <type_traits>  template <typename T> class A { public:     static_assert(std::is_default_constructible_v<T>);  };  struct B {    struct C {       int i = 0;    };      A<C> a_m; };  int main() {     A<B::C> a; } 

When compiling, a_m is not default constructible but a is.

When changing C to:

struct C {       int i;    }; 

everything is fine.

Tested with Clang 9.0.0.

like image 871
Nicolas Avatar asked Dec 09 '19 15:12

Nicolas


People also ask

Is default constructible C++?

A default constructible class is a class that has a default constructor (either its implicit constructor or a custom defined one). The is_default_constructible class inherits from integral_constant as being either true_type or false_type, depending on whether T is default constructible.

Is trivially default constructible?

A trivially default constructible type is a type which can be trivially constructed without arguments or initialization values, either cv-qualified or not. This includes scalar types, trivially default constructible classes and arrays of such types.

Does every class have a default constructor C++?

For example, all members of class type, and their class-type members, must have a default constructor and destructors that are accessible. All data members of reference type and all const members must have a default member initializer.

How many default constructors can a class have?

The following example defines a class with one constructor and two default constructors. You can declare default constructors as explicitly defaulted functions or deleted functions.


1 Answers

This is disallowed both by the text of the standard and by several major implementations as noted in the comments, but for completely unrelated reasons.

First, the "by the book" reason: the point of instantiation of A<C> is, according to the standard, immediately before the definition of B, and the point of instantiation of std::is_default_constructible<C> is immediately before that:

For a class template specialization, [...] if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.

Since C is clearly incomplete at that point, the behavior of instantiating std::is_default_constructible<C> is undefined. However, see core issue 287, which would change this rule.


In reality, this has to do with the NSDMI.

  • NSDMIs are weird because they get delayed parsing - or in standard parlance they are a "complete-class context".
  • Thus, that = 0 could in principle refer to things in B not yet declared, so the implementation can't really try to parse it until it has finished with B.
  • Completing a class necessitates the implicit declaration of special member functions, in particular the default constructor, as C doesn't have a constructor declared.
  • Parts of that declaration (constexpr-ness, noexcept-ness) depend on the properties of the NSDMI.
  • Thus, if the compiler can't parse the NSDMI, it can't complete the class.
  • As a result, at the point when it instantiates A<C>, it thinks that C is incomplete.

This whole area dealing with delayed-parsed regions is woefully underspecified, with accompanying implementation divergence. It may take a while before it gets cleaned up.

like image 86
T.C. Avatar answered Sep 21 '22 13:09

T.C.