Search code examples
c++c++20designated-initializer

Why I can not use designated initalizers with structs that are not aggregates?


C++ has a nice new feature:

struct Point{
int x;
int y;
int z; 
};

Point p{.x=47, .y=1701, .z=0};

But if I add a constructor then I am forbidden from using the nice designated initalizers syntax:

struct Point{
Point(int x, int y, int z = 0): x(x), y(y), z(z){}
int x;
int y;
int z; 
};

static Point p{.x=47, .y=1701, .z = 0};

error: designated initializers cannot be used with a non-aggregate type 'Point'

Am I missing something obvious(why it would be terrible if designated initalizers worked with structs/classes that have public members, but are not aggregates) or this is just a missing feature that is just not added to the standard?


Solution

  • Aggregate initialization (including initialization with designed initializers) circumvents the constructor of the class.

    This is not a problem for aggregates, since they aren't allowed to have user-defined constructors. But if you allow this kind of initialization for classes with user-provided constructors (that do something useful), it can be harmful.

    Consider this example:

    class A
    {
        static std::map<A *, int> &Indices()
        {
            static std::map<A *, int> ret;
            return ret;
        }
    
      public:
        int dummy = 0;
    
        A(int index)
        {
            Indices().emplace(this, index);
        }
    
        A(const A &) = delete;
        A &operator=(const A &) = delete;
        
        ~A()
        {
            auto it = Indices().find(this);
            std::cout << "Deleting #" << it->second << '\n';
            Indices().erase(it);
        }
    };
    

    If you were able to do A{.dummy = 42};, you'd get UB in the destructor, and would have no way to protect against this kind of usage.