Stepan Dyatkovskiy
Stepan Dyatkovskiy

Reputation: 990

C++11 Aggregate initialization of private member, is it correct?

Is it correct to initialize private member through aggregate initialization while passing it as a parameter into owner's class function? Just look at code below.

class A {
  struct S {
    int t, u;
  };
public:
  void f(const S& s) {}  
};

int main() {
  A a;
  a.f({1, 2}); // correct?
  return 0;
}

I checked standard and nets and it seems that there is no exact answer. Looks like mechanics are as follows: * braced initializer is public thing and thus user doesn't violate access restrictions. * implicit conversion from initializer into "S" is internal for "S" and thus also fine for compiler.

The question is, whether there is some reference in standard, draft or at least cppreference with the description of this behaviour?

Upvotes: 2

Views: 422

Answers (2)

Rakete1111
Rakete1111

Reputation: 48928

Yes this is correct. The only thing private about S is the name. Access control only controls access through the name ([class.access]p4). So you could use a type trait to get the type of S for example through f's type (example).

So, it is allowed because there is no restriction [dcl.init.agg] that prohibits initializing "private" types.

There is also a note in C++ draft, found by @Stepan Dyatkovskiy

Upvotes: 2

einpoklum
einpoklum

Reputation: 131445

It doesn't matter whether it's officially valid; you should avoid this corner case.

I would claim that "is it valid C++" is the wrong question here.

When you look at a piece of code and, try as you might, you can't decide whether it should be valid C++ or not; and you know it's going to be some corner case depending on the exact wording of the standard - it's usually a good idea not to rely on that corner case, either way. Why? Because other people will get confused too; they will waste time trying to figure out what you meant; they will go look it up in the standard - or worse, not look it up, and make invalid assumptions; and they will be distracted from what they actually need to focus on.

So, with this code, I would ask myself: "Is type S really private? Does outside code really not need to know about it?"

If the answer is "Yes, it is" - then I would change f, to take the parameters for an S constructor (and forward them to the ctor):

void f(int t, int u) { S {t, u}; /* etc. etc. */ }

If the answer is "No, code calling f() can know that it's passing an S reference" - then I would make S public.

Upvotes: -1

Related Questions