Default VS. Implicit Constructor in C++

Default vs. Implicit constructor in C++

The terms default and implicit, when talking about a constructor have the following meaning:

  • default constructor is a constructor that can be called with no arguments. It either takes no arguments or has default values for each of the arguments taken.

  • implicit constructor is a term commonly used to talk about two different concepts in the language, the

    • implicitly declared constructor which is a default or copy constructor that will be declared for all user classes if no user defined constructor is provided (default) or no copy constructor is provided (copy). That is, a class with no constructors declared by the user has one default constructor implicitly declared.

    • implicitly defined constructor is a implicitly declared constructor that is used (odr-used1 in the language and for which the compiler will provide a definition.

     

    struct test
    {
    test(int i = 0) { }
    // test(test const&) implicitly declared here
    };

    struct test2 { }; // implicitly declared: test2(), test2(test2 const&)

    int main()
    {
    test t;

    test copy(t); // causes *definition* of the implicitly
    // declared copy constructor

    test2 t2; // causes *definition* of test2::test2()

    test2 copy2(t2); // causes *definition* of test2::test2(test2 const&)
    }

In simple terms, a constructor is default if it can be called with no arguments. A constructor is implicit(ly declared/defined) if it is not provided by the user but declared/defined.

As of the specific cases:

Test t1;

Uses the default constructor, Test(int = 0), which is not implicit.

Test t2();

This is a strange quirk of the language, it declares a function that takes no arguments and returns a Test object.

Test t3 = 3;

This is called copy-initialization and is equivalent to the composition of an implicit* conversion from 3 to Test and copy construction of t3 from the result of the conversion. This will use the Test(int) constructor for the conversion, and then the implicitly defined (and declared) copy constructor. Note: the compiler can optimize away the copy, but it must verify that the copy constructor is available (access specifiers) and can be defined.

Test t4(4);

Uses the Test(int) constructor, which in this case is not acting as a default constructor.

Test t5 = Test(5);

Equivalent to the Test t3 = 3 case, with the only difference that the conversion from 5 to Test is explicit in this case. In this example it won't matter, but if the constructor had been marked as explicit this line would compile while the t3 case would fail.


*) Yet another use of implicit, in this case referring to the fact that the conversion from 3 to Test is not explicitly requested in the code. Compare this with t5 where the conversion is explicitly requested by the programmer: Test(5).

Defaulted constructor vs implicit constructor

The purpose of = default is to make the implicit definition explicit. Any differences between an implicitly defined version and the explicitly defaulted version are limited to some additional possibilities appearing due to the presence of an explicit declaration.

  1. The implicitly declared/defined constructor is always public, whereas the access control of the explicitly defined defaulted constructor is under your own control.

  2. Defining a defaulted default constructor enables you annotating it with attributes. For example:

    $ cat a.cpp 
    class A
    {
    public:
    [[deprecated]] A() = default;
    };

    int main()
    {
    A a;
    }

    $ g++ -std=c++14 a.cpp
    a.cpp: In function ‘int main()’:
    a.cpp:9:7: warning: ‘constexpr A::A()’ is deprecated [-Wdeprecated-declarations]
    A a;
    ^
    a.cpp:4:20: note: declared here
    [[deprecated]] A() = default;
    ^

What is the point of a C++ implicit default constructor?

The point of the implicit default constructor is the same point as of any other constructor.

Something needs to construct every instance of a given class. The class instance is not going to appear out of thin air, by itself. Something, somewhere, has the job of constructing the object.

If an explicit constructor is not declared, an implicit default constructor gets automatically defined, that default-constructs the class's superclasses, and any class members.

Here, "default-constructs" also includes the "do nothing" option, if the class member is a primitive type with no explicit constructor. So, in the end, the implicit default constructor may end up doing nothing. But it is still defined, if an explicit constructor is not specified (and if the implicit default constructor is not explicitly deleted, of course).

Implicit vs. Explicit Default Constructor Call

Quotes are from Standard p8.5/1

There is no user-defined constructor here, so, compiler will use the default one. And default constructor will not initialize members with zeros.

If no initializer is specified for an object, the object is
default-initialized.

To default-initialize an object of type T means:

If T is a (possibly cv-qualified) class type (Clause 9), constructors
are considered. The applicable constructors are enumerated (13.3.1.3),
and the best one for the initializer () is chosen through overload
resolution (13.3). The constructor thus selected is called, with an
empty argument list, to initialize the object.

In second case will be value-initialization.

An object whose initializer is an empty set of parentheses, i.e., (),
shall be value-initialized.

To value-initialize an object of type T means:

if T is a (possibly cv-qualified) class type without a user-provided
or deleted default constructor, then the object is zero-initialized
and the semantic constraints for default-initialization are checked,
and if T has a non-trivial default constructor, the object is
default-initialized;

To zero-initialize an object or reference of type T means:

if T is a (possibly cv-qualified) non-union class type, each
non-static data member and each base-class subobject is
zero-initialized and padding is initialized to zero bits;

Is there an implicit default constructor in C++?

If you do not define a constructor, the compiler will define a default constructor for you.

Construction

The implementation of this

default constructor is:

  • default construct the base class (if the base class does not have a default constructor, this is a compilation failure)
  • default construct each member variable in the order of declaration. (If a member does not have a default constructor, this is a compilation failure).

Note:

The POD data (int,float,pointer, etc.) do not have an explicit constructor but the default action is to do nothing (in the vane of C++ philosophy; we do not want to pay for something unless we explicitly ask for it).

Copy

If no destructor/copy Constructor/Copy Assignment operator is defined the compiler builds one of those for you (so a class always has a destructor/Copy Constructor/Assignment Operator (unless you cheat and explicitly declare one but don't define it)).

The default implementation is:

Destructor:

  • If user-defined destructor is defined, execute the code provided.
  • Call the destructor of each member in reverse order of declaration
  • Call the destructor of the base class.

Copy Constructor:

  • Call the Base class Copy Constructor.
  • Call the copy constructor for each member variable in the order of declaration.

Copy Assignment Operator:

  • Call the base class assignment operator
  • Call the copy assignment operator of each member variable in the order of declaration.
  • Return a reference to this.

Note Copy Construction/Assignment operator of POD Data is just copying the data (Hence the shallow copy problem associated with RAW pointers).

Move

If no destructor/copy Constructor/Copy Assignment/Move Constructor/Move Assignment operator is defined the compiler builds the move operators for you one of those for you.

The default implementation is:

Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:

Move Constructor:

  • Call the Base class Copy Constructor.
  • Call the move constructor for each member variable in the order of declaration.

Move Assignment Operator:

  • Call the base class assignment operator
  • Call the move assignment operator of each member variable in the order of declaration.
  • Return a reference to this.

User-defined constructors and implicit default constructors

"Default constructor" in this context means a constructor that can accept no arguments (either because it has no parameters or because it has only optional parameters).

"Implicit constructor" means a constructor generated for you automatically. This can be a no-args constructor, a copy constructor or (as of C++11) a move constructor.

So, there is no contradiction. When T is a class type with a user-provided constructor, then value initialization calls the default constructor. If this doesn't exist (because you defined another constructor but no no-args constructor), or if it is inaccessible (because you marked it private or protected and then used it from a place than cannot use such functions), then the value initialization is ill-formed and the compiler will reject your code. If it's declared but never defined then the program won't link.

Since C++11 the word "default" is probably somewhat ambiguous, since it's possible to define a copy constructor = default;



Related Topics



Leave a reply



Submit