Это обобщение, которое может вам пригодиться. Здесь переданные аргументы могут быть в любом порядке (даже пустом). Компилятор выведет, какие аргументы будут назначены тем членам данных рассматриваемого объекта (если вообще).
#include <iostream>
#include <tuple>
#include <string>
constexpr std::size_t NOT_FOUND = -1;
using default_tuple = std::tuple<int, bool, double, char, std::string>;
template <typename, std::size_t> struct Pair;
template <typename Tuple, typename T, std::size_t Start, typename = void>
struct Find : std::integral_constant<std::size_t, NOT_FOUND> {};
template <typename Tuple, typename T, std::size_t Start>
struct Find<Tuple, T, Start, std::enable_if_t<(Start < std::tuple_size<Tuple>::value)>> {
static constexpr size_t value = std::is_same<std::remove_reference_t<T>, std::tuple_element_t<Start, Tuple>>::value ? Start : Find<Tuple, T, Start+1>::value;
};
template <typename T, typename... Pairs> struct SearchPairs;
template <typename T>
struct SearchPairs<T> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename First, typename... Rest>
struct SearchPairs<T, First, Rest...> : SearchPairs<T, Rest...> {};
template <typename T, std::size_t I, typename... Rest>
struct SearchPairs<T, Pair<T,I>, Rest...> : std::integral_constant<std::size_t, I> {};
template <typename Tuple, typename ArgsTuple, std::size_t Start, std::size_t OriginalSize, typename Indices, typename LastIndices, typename = void>
struct ObtainIndices {
using type = Indices;
};
template <typename Tuple1, typename Tuple2, std::size_t Start, std::size_t OriginalSize, std::size_t... Is, typename... Pairs>
struct ObtainIndices<Tuple1, Tuple2, Start, OriginalSize, std::index_sequence<Is...>, std::tuple<Pairs...>,
std::enable_if_t<(Start < std::tuple_size<Tuple2>::value)>> {
using T = std::tuple_element_t<Start, Tuple2>;
static constexpr std::size_t start = SearchPairs<T, Pairs...>::value, // Searching through Pairs..., and will be 0 only if T is not found among the pairs. Else we start after where the last T was found in Tuple1.
index = Find<Tuple1, T, start>::value;
using type = std::conditional_t<(index < OriginalSize),
typename ObtainIndices<Tuple1, Tuple2, Start+1, OriginalSize, std::index_sequence<Is..., index>, std::tuple<Pair<T, index+1>, Pairs...>>::type, // Pair<T, index+1> because we start searching for T again (if ever) after position 'index'. Also, we must place Pair<T, index+1> before the Pairs... pack rather than after it because if a Pair with T already exists, that Pair must not be used again.
typename ObtainIndices<Tuple1, Tuple2, Start+1, OriginalSize, std::index_sequence<Is..., index>, std::tuple<Pair<T, index>, Pairs...>>::type // We add Pair<T, index> right before Pairs... since we want to use the default T value again if another T value is ever needed. Now this could clutter up the std::tuple<Pairs...> pack with many Pairs, causing more compile time, but there is no guarantee that Pair<T, index+1> is already there (since this default T value could be the first T value found).
>;
};
template <std::size_t I, std::size_t J, typename Tuple1, typename Tuple2>
void assignHelper (Tuple1&& tuple1, const Tuple2& tuple2) {
if (std::get<J>(tuple2) != std::tuple_element_t<J, Tuple2>()) // Make the assignment only if the right hand side is not the default value.
std::get<I>(std::forward<Tuple1>(tuple1)) = std::get<J>(tuple2);
}
template <typename Tuple1, typename Tuple2, std::size_t... Is, std::size_t... Js>
void assign (Tuple1&& tuple1, Tuple2&& tuple2, std::index_sequence<Is...>, std::index_sequence<Js...>) {
const int a[] = {(assignHelper<Is, Js>(tuple1, tuple2), 0)...};
static_cast<void>(a);
}
template <typename T, typename... Args>
void fillData (T& t, Args&&... args) {
auto s = t.tuple_ref();
std::tuple<Args...> a = std::tie(args...);
const auto tuple = std::tuple_cat(a, default_tuple{}); // Add default values for each type, in case they are needed if those types are absent in 'a'.
using IndexSequence = typename ObtainIndices<std::remove_const_t<decltype(tuple)>, decltype(s), 0, sizeof...(Args), std::index_sequence<>, std::tuple<>>::type;
assign (s, tuple, std::make_index_sequence<std::tuple_size<decltype(s)>::value>{}, IndexSequence{});
}
// Testing
class Thing {
int a, b;
char c;
double d;
std::string s;
int n;
public:
auto tuple_ref() {return std::tie(a, b, c, d, s, n);} // This (non-const) member function must be defined for any class that wants to be used in the 'fillData' function. Here 'auto' is std::tuple<int&, int&, char&, double&, std::string&, int&>.
void print() const {std::cout << "a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << ", s = " << s << ", n = " << n << '\n';}
};
int main() {
Thing thing;
fillData (thing, 5, 12.8);
thing.print(); // a = 5, b = uninitialized, c = uninitialized, d = 12.8, s = uninitialized, n = uninitialized
fillData (thing, 3.14, 2, 'p', std::string("hi"), 5, 'k', 5.8, 10, std::string("bye"), 9); // Note that std::string("hi") must be use instead of "hi" because "hi" is of type const char[2], not std::string (then the program would not compile). See my thread http://stackoverflow.com/questions/36223914/stdstring-type-lost-in-tuple/36224017#36224006
thing.print(); // a = 2, b = 5, c = p, d = 3.14, s = hi, n = 10
fillData (thing, 4.8, 8, 'q', std::string("hello"));
thing.print(); // a = 8, b = 5, c = q, d = 4.8, s = hello, n = 10
fillData (thing, std::string("game over"));
thing.print(); // a = 8, b = 5, c = q, d = 4.8, s = game over, n = 10
}
Похоже, проблема XY, хотя четко обозначены как X, так и Y. –