c++aggregate

Aggregate Initialization who has member initializer when initialization values is fewer than the number of members


As per the document, which states that[emphasise mine]:

Missing initializers in an initializer list

If an aggregate is initialized but the number of initialization values is fewer than the number of members, then all remaining members are initialized with an empty initializer list. In most cases, this will perform value-initialization on those members. We have discussed about the quotation here about how to understand it in the right way.

However, the aforementioned statement is not suitable when the class has default initliazers. Here is the example code below.

#include <iostream>
#include <type_traits>

struct Employee_no_default_member_initializer
{
    int id;
    int age;
    double wage;
};

struct Employee_with_default_member_initializer
{
    int id {1};
    int age {20};
    double wage {5.0};
};

template<typename T>
class is_allowed_type:public std::false_type{};

template<>
class is_allowed_type<Employee_with_default_member_initializer>:public std::true_type{};

template<>
class is_allowed_type<Employee_no_default_member_initializer>:public std::true_type{};

template<typename T, typename std::enable_if<is_allowed_type<T>::value, void>::type* = nullptr>
std::ostream& operator<<(std::ostream&os, const T& employee)
{
    os << "id="<< employee.id << std::endl;
    os << "age="<< employee.age << std::endl;
    os << "wage="<< employee.wage << std::endl;

    return os;
}

template<typename T, typename std::enable_if<is_allowed_type<T>::value,void>::type* = nullptr>
void foo()
{
    T frank = { 3 }; // copy-list initialization using braced list
    T joe { 2, 28};  // list initialization using braced list (preferred)
    std::cout << frank << std::endl;
    std::cout << joe << std::endl;
}

int main()
{
    std::cout << "#1" << std::endl;
    foo<Employee_with_default_member_initializer>();
    std::cout << "#2" << std::endl;
    foo<Employee_no_default_member_initializer>();

    return 0;
}

Here is the output:

#1
id=3
age=20
wage=5

id=2
age=28
wage=5

#2
id=3
age=0
wage=0

id=2
age=28
wage=0

Solution

  • tlrd; In case the braced init list has fewer element, the default member initializers will be used(if any). This means that for T frank = { 3 }; when T=Employee_with_default_member_initializer, the members age and wage will be initialized using 20 and 5.0 respectively.


    From the same article that you're referring to, the next chapter explains the case with default member initializers. It has the following example:

    In the previous lesson (13.8 -- Struct aggregate initialization) we noted that if an aggregate is initialized but the number of initialization values is fewer than the number of members, then all remaining members will be value-initialized. However, if a default member initializer is provided for a given member, that default member initializer will be used instead.

    struct Something
    {
       int x;       // no default initialization value (bad)
       int y {};    // value-initialized by default
       int z { 2 }; // explicit default value
    };
    
    int main()
    {
       Something s3 {}; // value initialize s3.x, use default values for s3.y and s3.z
    
       return 0;
    }
    

    In the above case, s3 is list initialized with an empty list, so all initializers are missing. This means that a default member initializer will be used if it exists, and value initialization will occur otherwise. Thus, s3.x (which has no default member initializer) is value initialized to 0, s3.y is value initialized by default to 0, and s3.z is defaulted to value 2.