The answers to the question on how to avoid undefined execution order for the constructors when using std::make_tuple led to a discussion during which I learned that the ord
The solution I had come up uses std::tuple<...> to put the arguments together than calls a function object using the elements of this object. The advantage is that it can deduce the return type. The actual specific logic looks like this:
template
auto function_apply(F&& f, T&& t, indices const*)
-> decltype(f(std::get(t)...)) {
f(std::get(t)...);
}
template
auto function_apply(F&& f, T&& t)
-> decltype(function_apply(std::forward(f), std::forward(t),
make_indices())) {
function_apply(std::forward(f), std::forward(t),
make_indices());
}
... which is called using an expression like this:
void f(int i, double d, bool b) {
std::cout << "i=" << i << " d=" << d << " b=" << b << '\n';
}
int fi() { std::cout << "int\n"; return 1; }
double fd() { std::cout << "double\n"; return 2.1; }
bool fb() { std::cout << "bool\n"; return true; }
int main()
{
std::cout << std::boolalpha;
function_apply(&f, std::tuple{ fi(), fd(), fb() });
}
The main disadvantage is that this approach requires the specification of the std::tuple<...>'s elements. Another problem is that the current version of gcc on MacOS calls the functions in the opposite order they appear, i.e., the order of evaluation in a braced-init-list isn't obeyed (a gcc bug) or doesn't exist (i.e., I misunderstood the guarantees of using a braced-init-list. clang on the same platform executes the functions in the expected order.
The used function make_indices() just creates a suitable pointer to an object of type indices with a list of indices usable with a std::tuple<...>:
template struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template
struct indices<0, Indices...>
{
typedef indices<0, Indices...> type;
};
template
struct indices
{
typedef typename indices::type type;
};
template
typename indices::value - 1>::type const*
make_indices()
{
return 0;
}