3

I have this code:

template<typename ...T>
struct Test
{
    void call(string str)
    {
        abc(get<T>(str)...);
    }

    template<typename U>
    string get(string& inp)
    {
        string ret{ inp[0] };
        inp.erase(0, 1);

        cout << ret << endl; // first "a", next "b", next "c" - everything is ok

        return ret;
    }

    void abc(string a, string b, string c)
    {
        cout << a << " " << b << " " << c << endl; // "b c a" - why?
    }
};

I'm calling it like this:

Test<int, bool, float> test;
test.call("abc");

And the output is b c a thought I expect a b c. Moreover in get() function I have a correct order. Why is this? I can't find any rule about this order.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
nikitablack
  • 4,359
  • 2
  • 35
  • 68
  • 3
    Possible duplicate of [Order of evaluation in C++ function parameters](http://stackoverflow.com/questions/2934904/order-of-evaluation-in-c-function-parameters) – James Adkison Feb 08 '16 at 13:37
  • look in http://en.cppreference.com/w/cpp/language/eval_order under the undefined behavior section. What seems to me like the most relevant to your case is the example: cout << i << i++; which is undefined. I think each compiler can choose in which order to evaluate the variables. – Jonathan Feb 08 '16 at 13:40
  • I don't think this should be closed as a duplicate as the solution to this problem is very different for template and non-template code. – TartanLlama Feb 08 '16 at 13:45

2 Answers2

5

The order of evaluation of function arguments is unspecified.

abc(get<T>(str)...);

That is essentially the same as:

abc(get<T1>(str), get<T2>(str), get<TN>(str));

You could enforce evaluation order by generating an array to store the strings, then dispatching from that array:

template <std::size_t N, std::size_t... Idx>
void call_helper(std::array<std::string, N> arr, std::index_sequence<Idx...>) {
    abc(std::get<Idx>(arr)...);
}

void call(string str)
{
    std::array<std::string,sizeof...(T)> arr { get<T>(str)... }; 
    call_helper(arr, std::index_sequence_for<T...>{});
}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Can you please explain, why in array creation the order is strict? What are `index_sequence` and `index_sequence_for`? This types are undefined int VS 2013. – nikitablack Feb 08 '16 at 13:55
  • Oh, I didn't noticed they are available starting from c++14 – nikitablack Feb 08 '16 at 13:57
  • 1
    @nikitablack: The order of evaluation of brace-initializers is defined because the standard says so, nothing more to it. The two symbols you ask about are [part of the standard library](http://en.cppreference.com/w/cpp/utility/integer_sequence). – Deduplicator Feb 08 '16 at 13:57
  • 1
    @nikitablack Initializer-clauses in initializer-lists are specified to be evaluated in the order they appear. `std::index_sequence` is from C++14, but you can find implementations online for C++11. Like [these](http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence). – TartanLlama Feb 08 '16 at 13:59
  • @TartanLlama Thank you. But still confused - isn't this line `abc(std::get(arr)...);` is the same as I had in the beginning? It again looks like as an unspecified order. – nikitablack Feb 08 '16 at 14:02
  • 2
    @nikitablack Yes, the evaluation order of those `std::get` calls is unspecified, but that doesn't matter. They're just getting the `N`th element of the array, they don't have any side effects. All the processing has already been done in the generation of the array. – TartanLlama Feb 08 '16 at 14:05
  • 1
    @TartanLlama But why. It can happen that after unpacking it will look `abc(std::get<2>(arr), std::get<0>(arr), std::get<1>(arr))`, can it? Upd. oh, sorry, I wrote and later realized that I wrote a bullshit :). Of cource it will be only `abc(std::get<0>(arr), std::get<1>(arr), std::get<2>(arr))`. Thanks a lot! – nikitablack Feb 08 '16 at 14:09
  • 2
    @nikitablack It can help to think of it this way: mutating operations (operations that change their data) on the same data in a function call are a *bad idea*, because we don't have order. So no `pop` or `push` or `++` -- these are all bad. Non-mutating operations, like `get`, `front`, `[i]` are pretty problem-free. (some mutating operations do not care about order, but have caution -- ie, `set.insert(x)` is safe-ish, but is scary.) Knowing *why* mutation in function arguments is bad is useful, but the rule of thumb (mutation=danger) makes it easier to think about. – Yakk - Adam Nevraumont Feb 08 '16 at 15:42
2

The order of function call arguments are not guaranteed. Therefore, abc(get<T>(str)...); doesn't have a defined order.

See Order of evaluation in C++ function parameters for more details.

Community
  • 1
  • 1
James Adkison
  • 9,412
  • 2
  • 29
  • 43