I want to fill a tuple<Ts...>
from a vector
of variant
s. (NB: This question follows up on Creating a tuple from a folding expression return values , see 2nd comment to the answer )
Expanding the pack in the template argument list for the tuple
(the return type) works as expected. But expansion of the pack in the function argument list (of make_tuple()
in this case) seems to reverse the order of types - when compiled with gcc
.
Pls consider the following mre (also on compiler explorer) - after studying cppreference I'd expect in_order
to work and reversed
to fail:
#include <iostream>
#include <tuple>
#include <typeinfo>
#include <variant>
#include <vector>
using DATA=std::vector<std::variant<int,double>>;
template <typename... Ts> std::tuple<Ts...> in_order(DATA const &args) {
try {
auto arg_iter = args.cbegin();
return std::make_tuple(std::get<Ts>(*arg_iter++)...);
} catch(std::bad_variant_access const &e) {
std::cout << "in order fails\n";
return std::make_tuple(Ts{0}...);
}
}
template <typename... Ts> std::tuple<Ts...> reversed(DATA const &args) {
try {
auto arg_iter = args.cbegin();
std::advance(arg_iter,args.size());
return std::make_tuple(std::get<Ts>(*(--arg_iter))...);
} catch(std::bad_variant_access const &e) {
std::cout << "reversed fails\n";
return std::make_tuple(Ts{0}...);
}
}
int main(int argc,char **argv) {
DATA data;
data.push_back(3.3);
data.push_back(7);
auto const &[d0,i0] = in_order<double,int>(data);
std::cout << "in order: " << typeid(decltype(d0)).name() << d0 << " , " << typeid(decltype(i0)).name() << i0 << "\n";
auto const &[d1,i1] = reversed<double,int>(data);
std::cout << "reversed: " << typeid(decltype(d1)).name() << d1 << " , " << typeid(decltype(i1)).name() << i1 << "\n";
}
When compiled with clang
on compiler explorer, the behaviour is exactly as expected. But when using gcc
, it's the other way around: in_order
fails while reversed
returns the desired result. As we use gcc
in our project, I need gcc
the behave correctly.
My question is: which behaviour is the correct one,
clang
is correctgcc
is correctOr, phrased slightly different: is one of the compilers buggy in the case?
When you do
return std::make_tuple(std::get<Ts>(*arg_iter++)...);
you are calling a function and the order of evaluation for function parameters is not specified so both compilers are correct.
To get both compilers to do the same thing we can just directly construct the tuple
from the pack expansion like
return std::tuple{std::get<Ts>(*(--arg_iter))...};
This works because the order of initializers is specified and goes from left to right. You can see both compilers give the same results in this live example.