Why Is My Class Non Default-Constructible

Why is my class non default-constructible?

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.

How to properly initialize non-default-constructible class member?

The best idea will be to create helper function, that will calculate something and then just initialize m_foo in constructor initialized list.

class Bar {
private:
Foo m_foo;
public:
Bar( /* ... */ ) : m_foo(calculate_something()) {
}
private:
static int calculate_something()
{
int something = 0;
// lot of code to calculate something
return something;
}
};

No default constructor for a class type

What does "no" default constructor mean? Don't class types always have one at least implicitly defined (or it is deleted)?

In case there's a user defined constructor, there's no implicitely defined default constructor (with no arguments).

The easiest way to declare one is to use the default keyword:

class MyClass {
public:
MyClass(int y); // <<< No default constructor generated
MyClass() = default; // <<< Force generation of default constructor
};

std::vector works with classes that are not default constructible?

The requirement in C++03 is that types being stored in a container be CopyConstructible and Assignable (see §23.1 Container Requirements). However, in C++11 these requirements are relaxed, and tend to apply to the operations performed on the container. So a simple default construction has no requirements (see teble 96, §23.1 in C++11 standard).

As soon as you try to copy a vector, or insert elements into it, you will meet the CopyInsertable, CopyAssignable, EmplaceConstructible, MoveInsertable, MoveAssignable etc. requirements

C++ no default constructor error when passing one class into another's constructor

You should be initializing via initialization lists, not via assignment.

// You probably want these to be a struct, not class.
// This way all members are public by default.
struct Foo {
int size;
char name;

Foo(int s,char n) : size{s}, name{n} {}
};

struct Bar {
int size;
char name;
Foo foo;

Bar(int s, char n, const Foo &f) : size{s}, name{n}, foo{f} {}
};

When you don't initialize member variables in the initialization list, that's the same as default-constructing it and then re-assigning the values. So you are doing twice the work for no reason. (Also see Constructors and member initializer lists)

Because you have defined a constructor for your structs, the default constructor is implicitly deleted which caused your compilation error.

On a side note, you might not even need these constructors and can use aggregate initialization instead, like this:

struct Foo {
int size;
char name;
};

void example() {
Foo foo = {1, 'a'}; // the = is optional
}

Is it possible to create a template instance with a non-default constructor in c++?

template <class ActorStatsClass>
class BaseActor
{
public:
BaseActor()
{

}

private:
ActorStatsClass m_stats;
};

If this BaseActor class is going to have arbitrary members, it needs ways to construct those arbitrary members. So it needs a generic constructor.

template <class ActorStatsClass>
class BaseActor
{
public:
template<class...Us>
BaseActor(Us&&...vs)
: m_stats(std::forward<Us>(vs)...)
{ }

private:
ActorStatsClass m_stats;
};

This uses forwarding, that whatever parameters are passed to BaseActor, it just passes them directly to constructing the m_stats member.

This allows:

class PlayerActor : public BaseActor<PlayerStats> {
public:
PlayerActor(const bool is_new)
: BaseActor<PlayerStats>(is_new)
{ }
PlayerActor() = delete; // Note: this is better than making it private
};

Can const-default-constructible objects be of non-class types?

Is the type of S::I said to be non-const-default-constructible?

Yes. Like you've quoted in [dcl.init]/7 only class types can be const-default-constructible. The reason for this is non-class types do not have a default constructor, meaning they have no default value that can be used if they are declared like

const T foo;

When you read

any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer is not const-default-constructible ([dcl.init])

It is saying that if you have a member in the form of const T name; in your class then the default constructor is deleted if T is not const-default-constructible. In your case that means your constructor is deleted because const int is not const-default-constructible.


In the case of X, M is still not const-default-constructible because it is not a class type. X though is const-default-constructible because M has a brace-or-equal-initializer so [class.default.ctor]/2.4 does not apply.


This can be boiled down into a simple rule: All const objects must have an initializer

Since built in types do not get default initialized they must have a value provided by the programmer.



Related Topics



Leave a reply



Submit