Search code examples
c++constructorc++17aggregatelist-initialization

Does value-initialization use the implicit default constructor?


According to this site /link/:

If the default constructor is explicitly declared but marked as deleted, empty brace initialization can't be used:

and it also gives an example to this:

class class_f {
public:
    class_f() = delete;
    class_f(string x): m_string { x } {} // if it is deleted, there will be no errors.
    string m_string;
};

int main()
{
    class_f cf{ "hello" };
    class_f cf1{}; // compiler error C2280: attempting to reference a deleted function
}

What I don't really understand is that, if there is no user-provided constructor, there will be no more error, even if the deleted default constructor is still there. As far as I know, if there is a user-provided constructor, there will be no implicit default constructors, but the default constructor is already deleted. So I don't know what is called in case of the value-initialization and why it is works in the example below:

#include <string>

class class_f {
public:
    class_f() = delete;
    std::string m_string;
};
int main()
{
    class_f cf1{}; // Does the implicit-default constructor is called here? But, it is deleted or not?
}

Solution

  • There is a difference between the C++ 17 Standard and the C++ 20 Standard according to the definition of aggregates.

    According to the C++ 17 Standard this declaration

    class class_f {
    public:
        class_f() = delete;
        std::string m_string;
    };
    

    declares an aggregate that you may initialize using braces.

    From the C++ 17 Standard (11.6.1 Aggregates)

    1 An aggregate is an array or a class (Clause 12) with

    (1.1) — no user-provided, explicit, or inherited constructors (15.1),

    (1.2) — no private or protected non-static data members (Clause 14),

    (1.3) — no virtual functions (13.3), and

    (1.4) — no virtual, private, or protected base classes (13.1).

    According to the C++ 20 Standard this declaration does not declare an aggregate and compiler will issue an error relative to the object initialization.

    From the C++ 20 Standard (9.4.2 Aggregates)

    1 An aggregate is an array or a class (Clause 11) with

    (1.1) — no user-declared or inherited constructors (11.4.5),

    (1.2) — no private or protected direct non-static data members (11.9),

    (1.3) — no virtual functions (11.7.3), and

    (1.4) — no virtual, private, or protected base classes (11.7.2).

    You can try the following demonstration program

    #include <iostream>
    #include <iomanip>
    #include <type_traits>
    
    class class_f {
    public:
        class_f() = delete;
        std::string m_string;
    };
    
    int main()
    {
        std::cout << "std::is_aggregate_v<class_f> = " <<
            std::boolalpha << std::is_aggregate_v<class_f> << '\n';
    }
    

    Run it setting a compiler option to support C++ 17 and then to support C++ 20.

    The type trait std::is_aggregate was introduced in C++ 17.