Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

What differs between parentheses and curly in std::tuple?

Well today I met a werid behavior when using std::tuple() and std::tuple{}.

Here is an easy demo:

#include <iostream>
#include <tuple>

#define   rd   ({ int       x = (std::cin >> x, x); x; })

template <typename... Args>
void log(const Args &...args) { ((::std::cout << args << ", "), ...); }

auto main() -> int {

    auto [a, b] = std::tuple(rd, rd);
    auto [c, d] = std::tuple{rd, rd};

    log(a, b, c, d);

    return {};
}

Run:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

 echo '1 2 3 4' > input
 clang++ -std=c++17 demon.cpp
 ./a.out < input
 g++ -std=c++17 demon.cpp
 ./a.out < input
 cat demon.cpp

And I got 2 different results: 1, 2, 3, 4 and 2, 1, 3, 4. That’s out of my expectation. I turned to my friends and got the reply of "The macro rd is a unspecific behaivor". However, I am still confused with this answer after learning something about unspecific behaivor.

>Solution :

Your friends are correct.

The unspecified behaviour occurs because the evaluation order of function arguments is not specified, it can happen in any order and can change between calls. Meaning the order of std::cin is not guaranteed. The compiler is free to reorder them as it sees fit. This applies to std::tuple(), hence the incorrect output.

On the other hand for braced-init-list, the order is fixed left-to-right, therefore std::tuple{} is safe and the output is guaranteed.

Furthermore, in rd, having braced-statements in expressions is not standard C++ so you are at the mercy of your compiler. I would seriously recommend macro-less refactoring, hiding std::cin in an expression seems like asking for trouble.

Why not simple read_int() function or generic read_T? Then you can generalize to read_Ts<T1,T2,T3...> with explicit sequencing. For example:

template<typename T>
T read_T(){
    T x;
    std::cin>> x;
    return x;
}

template<typename...T>
auto read_Ts(){
    return std::tuple{read_T<T>()...};
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading