I am a big fan of using ad-hoc structs and designated initializers to emulate named parameters for my functions.
struct Args {
int a;
int b;
};
int f(Args a) {
return a.a + a.b;
}
int g() {
return f({.a = 1, .b = 2}); // Works! That's what I want.
return f({1, 2}); // Also works. I want to forbid this, though.
}
However, those structs can still be initialized by a positional initializer list. I.e., you can still call f({1, 2})
.
I want to forbid this and force the caller to explicitly name the parameters also at the call-site. How can I do so in C++20?
The best way is just code review. Tell people to use designated initializers. Designated initializers are awesome, people like using them. This is really more of a social problem than a technical one.
If you really want to give a nudge, you could always stick in some truly awful data members first, like so:
class Args {
struct Key { explicit Key() = default; };
struct StopIt { StopIt(Key) { } };
public:
StopIt asdfjkhasdkljfhasdf = Key();
int a;
int b;
};
Args
is still an aggregate, we just have this extra leading data member spelled with whatever came out when I just banged on my keyboard. It has a default member initializer - however that initializer is private to Args
and only Args
knows how to construct it.
So the user cannot† correctly provide an initializer for that particular member, they have to rely on the default member initializer to initialize it correctly. And since it goes first, they have to rely on designated initialization in order to be able to initialize any of the other members.
But also... this is kind of a silly thing to write? Just tell people to use designated initialization.
†Technically, they could write f({Args().asdfjkhasdkljfhasdf, 1, 2})
in this case, but this seems like we're going for absurdity or spite at this point.