Reputation: 33
I'm playing around with variadic templates and fold expressions, in particular doing type conversions to put into function parameters. My understanding is that to do something like:
template<T, typename ... Args>
void convertAndEvaluate(const vector<T>& convertibles)
{
size_t i = 0;
evaluate(some_function<Args>(convertibles[i++])...);
}
would not work as the order of evaluation of the function inputs are unspecified. Fold expressions can give the right evaluation order, however their result is parentheses enclosed and cannot be used as a function input. I can achieve the same result with index_sequences through another templated function, but I was wondering if there was a more concise way with C++17, something like using constexpr with pack expansion.
Toy example:
#include <iostream>
#include <vector>
#include <utility>
using namespace std;
template<typename ... Args>
class Foo {
public:
Foo() {}
void just_print(const std::vector<int>& convertible)
{
size_t i = 0;
((cout << static_cast<Args>(convertible[i++]) << " "), ...);
cout << endl;
}
template<typename T,T... ints>
void expandEvaluate(const std::vector<int>& values, std::integer_sequence<T, ints...> int_seq)
{
eval(static_cast<Args>(values[ints])...);
}
void convert(const std::vector<int>& convertible)
{
expandEvaluate(convertible, std::make_index_sequence<sizeof...(Args)>());
}
void convert_wrong(const std::vector<int>& convertible)
{
size_t i = 0;
eval(static_cast<Args>(convertible[i++])...);
}
void eval(const Args&... values)
{
((cout << values << " "), ...);
cout << endl;
}
};
int main()
{
Foo<double, int, float, int, double> bar;
bar.eval(3, 4, 5, 6, 7);
bar.just_print({3, 4, 5, 6, 7});
bar.convert_wrong({3, 4, 5, 6, 7});
bar.convert({3, 4, 5, 6, 7});
return 0;
}
Output:
3 4 5 6 7
3 4 5 6 7
7 6 5 4 3
3 4 5 6 7
Edit: In retrospect, my solution with the integer expansion necessitates expanding two parameter packs simultaneously, is this defined in the standard?
Upvotes: 3
Views: 784
Reputation: 66200
I think your solution (use of std::make_index_sequence
/std::index_sequence
to get indexes in right order) is a good one (and works also with C++14).
Starting from C++17 you can also use std::tuple
/std::apply()
void convert2 (std::vector<int> const & cv)
{
std::size_t i{};
std::tuple t{ static_cast<Args>(cv[i++])... };
std::apply([=](auto ... args){ eval(args...); }, t);
}
but it's almost your std::make_index_sequence
/std::index_sequence
solution, wrapped by std::apply()
.
Upvotes: 2