How to guarantee order of argument evaluation when calling a function object?

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 order of argument evaluation can be guaranteed for constructors: Using a braced-init-list the order is guaranteed to be left to right:

T{ a, b, c }

The expressions a, b, and c are evaluated in the given order. This is the case, even if the type T just has a normal constructor defined.

Clearly, not everything called is a constructor and sometimes it would be nice to guarantee the order of evaluation when calling a function but there is no such thing as brace-argument-list to call function with a defined order of evaluation of their arguments. The question becomes: Can the guarantees to constructors be used to build a function call facility ("function_apply()") with an ordering guarantee for the evaluation of arguments? It is acceptable to require a function object being called.


What about a silly wrapper class like this:

struct OrderedCall
{
    template <typename F, typename ...Args>
    OrderedCall(F && f, Args &&... args)
    {
        std::forward<F>(f)(std::forward<Args>(args)...);
    }
};

Usage:

void foo(int, char, bool);

OrderedCall{foo, 5, 'x', false};

If you want a return value, you could pass it in by reference (you'll need some trait to extract the return type), or store it in the object, to get an interface like:

auto x = OrderedCall{foo, 5, 'x', false}.get_result();

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 <typename F, typename T, int... I>
auto function_apply(F&& f, T&& t, indices<I...> const*)
    -> decltype(f(std::get<I>(t)...)) {
    f(std::get<I>(t)...);
}

template <typename F, typename T>
auto function_apply(F&& f, T&& t)
    -> decltype(function_apply(std::forward<F>(f), std::forward<T>(t),
                               make_indices<T>())) {
    function_apply(std::forward<F>(f), std::forward<T>(t),
                   make_indices<T>());
}

... 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<int, double, bool>{ 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<I...> with a list of indices usable with a std::tuple<...>:

template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
    typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
    typedef typename indices<Index - 1, Index, Indices...>::type type;
};

template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices()
{
    return 0;
}