This is a c++17 answer:
template<auto PMem>
struct member_type {};
template<class T, class M, M(T::*ptr)>
struct member_type<ptr> { using type=M; };
template<auto PMem>
using member_type_t=typename member_type<PMem>::type;
template<class T, auto...PMem>
T func( member_type_t<PMem>... args ) {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
}
test code:
struct Bob {
int x,y;
};
int main() {
Bob b = func<Bob, &Bob::x, &Bob::y>( 2, 3 );
(void)b;
}
live example.
You can also perfect forward without matching types. This has the downside that this doesn't work:
struct A {
int x, y;
};
struct B {
A one, two;
};
B func<B, &B::one, &B::two>( {1,2}, {3,4} );
but it does eliminate some boilerplate above, and it could remove a redundant move per member field.
To do that, simply drop the member_type helper completely:
template<class T, auto...PMem, class...Args>
T func( Args&&... args ) {
T retval = {};
( ((retval.*PMem) = std::forward<Args>(args)), ... );
return retval;
}
Doing this outside of c++17 is a pain. You lack auto parameters and ... expansion of statements. The second is relatively easy to work around with some boilerplate, but the first makes your desired syntax basically impossible; you may be reduced to using macros.
If you don't want <> syntax:
template<class T, auto...PMem>
constexpr auto make_func() {
return +[]( member_type_t<PMem>... args )->T {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
};
}
struct Bob {
int x,y;
};
constexpr auto* func = make_func<Bob, &Bob::x, &Bob::y>();
Live example.
A constexpr function pointer should be treated nearly indistinguishably from a function, except overloading isn't available.
In MSVC you might have to disambiguate the function pointer type like this:
template<class T, auto...PMem>
using func_t = T(*)(member_type_t<PMem>...);
template<class T, auto...PMem>
constexpr func_t<T, PMem...> make_func() {
return []( member_type_t<PMem>... args )->T {
T retval = {};
( ((retval.*PMem) = std::forward<member_type_t<PMem>>(args)), ... );
return retval;
};
}
Live example.
Sometimes MSVC has problems with unary operator + on stateless lambdas having multiple different calling convention optoins. The above avoids that issue, at the cost of a bit of boilerplate.