I'm looking to get some clarity on the correct way to forward multiple members from the same forwarding reference to a struct.
Example
Here's an example of forwarding two fields (one_field
and another_field
) to a class constructor (widget
) from a forwarding reference argument info
. This is how I previously thought we should do it:
template<typename Bundle>
auto make_widget(Bundle&& info) {
return widget(std::forward<Bundle>(info).one_field, std::forward<Bundle>(info).another_field);
}
There is a discussion on reddit's C thread that lists 9 ways to forward struct member fields. This confirms that it is OK to use this idiom to forward individual member fields, but this seems problematic for multiple uses of forward
.
FWD(t).field: forward t.field, and invalidates t and all its members
What concerns me is that my example therefore is incorrect, as all of the fields of info
are invalidated by the initial std::forward<Bundle>(info)
. The subsequent field access of the second std::forward<Bundle>(info)
. So I am now unclear on the validity of the example above.
How should we be passing on multiple struct fields in a way which is theoretically and practically safe? I'm very much hoping it's not required to write a variant of std::forward
which moves its argument depending on the semantics of the parent struct type.
References
Reddit discussion on single field members: https://www.reddit.com/r/cpp/comments/q4mchr/overview_of_different_ways_of_passing_struct/
Comment on multiple forwards: https://www.reddit.com/r/cpp/comments/q4mchr/comment/hfzsidd/ -- found this post suggesting this is OK "in practice" but given the subtlety of C bugs, I'm keen to know this is OK "in theory" as well! I'm hoping that since under the hood std::forward
is a static_cast
to an rvalue type the invalidation doesn't happen to the other fields as would happen if a move constructor was actually called.
Similar SO question (for a single member field): How to perfectly forward a struct member?
Thanks all.
CodePudding user response:
the forward you have already does the correct thing (i.e. forward the member as Bundle) and access a field is not like to invalidate the parent object.
template<typename Bundle>
auto make_widget(Bundle&& info) {
return widget(
std::forward<Bundle>(info).one_field,
std::forward<Bundle>(info).another_field
);
}
if you want, you can also use something like std::forward_like
in c 23
template<typename Bundle>
auto make_widget(Bundle&& info) {
return widget(
std::forward_like<Bundle>(info.one_field),
std::forward_like<Bundle>(info.another_field)
);
}