Aggregate initialization by-passes private class constructor
Asked Answered
T

3

5

I always thought that aggregate initialization was to save coders from writing custom constructors. However, this seems to have "sneaked in" a "security by-pass" for private constructors.

Say I have class A which I only want to be created by class B.

struct A
{
  friend class B;
  const int i, k;
private:
  A () = default;
};

class B
{
public:
  A what () { return {1, 2}; }
};

int main ()
{
  B b {};
  A a {2,3}; // oh no
  return 0;
}

Above example compiles and runs fine, and through the use of braces I can very easily create an A object anywhere.

The only way to prevent this is to write a user constructor, which then cancels out aggregate initialization altogether.

So my question is: is aggregate initialization a "hidden" constructor that is by default public?

Live example: https://onlinegdb.com/r1jHLxzRD

Similar question with no answer: Private aggregate initialization

Trant answered 5/1, 2021 at 14:23 Comment(5)
You only get aggregate initialization when your class is an aggregate. To know when that is, see: en.cppreference.com/w/cpp/language/aggregate_initializationOviform
Sooo add protected: A(int i, int k) : i(i),k(k){}?Rhyne
@Rhyne I don't like it cause 1) I have to write it :-) 2) I won't be able to use {i,k} anymore, only A(i,k) (DOESN'T APPLY)Trant
I won't be able to use {i,k} you should be able to.Rhyne
@Rhyne You're right! onlinegdb.com/HygVRYgGRD So, only (1) stands (laziness - which IMHO was the purpose of {} init... but anyway)Trant
P
5

I always thought that aggregate initialization was to save coders from writing custom constructors.

That's something that aggregate initialisation achieves, but defining that as the purpose of aggregate initialisation is overly reductive.

However, this seems to have "sneaked in" a "security by-pass" for private constructors.

This sneaky case of aggregates with private (or even deleted!) default constructor was introduced in C++11 through introduction of defaulted (and deleted) member functions. It no longer exits in C++20 where A is no longer an aggregate.

So my question is: is aggregate initialization a "hidden" constructor

I would describe aggregate initialisation as not using a constructor at all. Instead, members are initialised directly.

Prefatory answered 5/1, 2021 at 14:35 Comment(2)
I would describe aggregate initialisation as not using a constructor at all which is just the 1st part of what constructors do, 2nd part being running some user-defined code. Right? Maybe I'm not phrasing it accurately... defining that as the purpose of aggregate initialisation is overly reductive so what was the biggest incentive to introduce them?Trant
@BillKotsias Constructor is a function generated by the compiler which first indeed initialises the sub-objects and then executes the body. With aggregate initialisation, there is no function i.e. no constructor being called. so what was the biggest incentive to introduce them? Well, compatibility with C is another big thing, at least originally.Prefatory
J
1

Since C++20 this type is no longer an aggregate, so this program no longer compiles.

Manually adding any constructor to a class now prevents it from being an aggregate.

Jelle answered 14/9, 2023 at 15:5 Comment(0)
B
0

You can portably disable aggregate initialization by making the default constructor explicit:

explicit  A () = default;

This works on C++11, C++14, C++17, C++20, and C++23.

Bonaventure answered 14/9, 2023 at 15:0 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.