So I'm trying to figure out how this works: C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?
The piece of black magic I do not understand is this code fragment:
f(std::get<N>(std::forward<Tuple>(t))...)
it's the expression inside f that I don't understand.
I understand that the expression somehow unpacks/expands what's inside t into a list of arguments. But could someone care to explain how this is done? When I look at the definition of std::get (http://en.cppreference.com/w/cpp/utility/tuple/get), I don't see how N fits in...? As far as I can tell, N is a sequence of integers.
Based on what I can observe, I'm assuming that expressions in the form E<X>... where X is the sequence of types X1. X2, ... Xn, the expression will be expanded as E<X1>, E<X2> ... E<Xn>. Is this how it works?
Edit: In this case N is not a sequence of types, but integers. But I'm guessing this language construct applies to both types and values.
I think that #Xeo's comment summed it up well. From 14.5.3 of the C++11 standard:
A pack expansion consists of a pattern and an ellipsis, the
instantiation of which produces zero or more instantiations of the
pattern in a list.
In your case, by the time you finish with the recursive template instantiation and end up in the partial specialization, you have
f(std::get<N>(std::forward<Tuple>(t))...);
...where N is parameter pack of four ints (0, 1, 2, and 3). From the standardese above, the pattern here is
std::get<N>(std::forward<Tuple>(t))
The application of the ... ellipsis to the above pattern causes it to be expanded into four instantiations in list form, i.e.
f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
The fundamental ingredient to expanding the std::tuple<T...> is actually omitted from the code: you need to obtain a a second parameter back: in addition to the list of types of the std::tuple<...> you need a parameter pack with indices 0, 1, ..., n. Once you have these two parameters packs, you can expand them in tandem:
template <typename F, typename... T, int... N>
void call_impl(F&& fun, std::tuple<T...>&& t) {
fun(std::get<N>(t)...);
}
The real magic lies in conjuring up the second parameter pack when you just have a std::tuple<T...>. It takes a bit of template programming. Here is an approach to create the list of indices:
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;
}
So, if you have a function template, let's call it call() which takes a function object and a std::tuple<T...> with the arguments to the function. An easy approach is to rewrite the call_impl() mentioned above to deal with deducing the indices:
template <typename F, typename Tuple, int... N>
void call_impl(F&& fun, Tuple&& t, indices<Indices...> const*)
{
fun(std::get<N>(t)...);
}
template <typename F, typename Tuple>
void call(F&& fun, Tuple&& t)
{
call_imle(std::forward<F>(fun), std::forward<Tuple>(t), make_indices<Tuple>());
}
What this code doesn't really extend is the correct use of std::forward<...>() with the various std::tuple<...> elements when calling the function. Just using std::forward<Tuple>(t) does not work because it possibly moves the entire std::tuple<...> rather than moving the elements. I think something like a suitable element-wise move of a std::tuple<...> can be done but I haven't done it, yet.
Related
Here's the code for std::make_tuple in the standard library.
template<typename... _Elements>
inline tuple<typename __decay_and_strip<_Elements>::__type...>
make_tuple(_Elements&&... __args)
{
typedef tuple<typename __decay_and_strip<_Elements>::__type...>
__result_type;
return __result_type(std::forward<_Elements>(__args)...);
}
What I would like to do, is to sort __args before creating the tuple, presumably with std::sort(..., Compare comp) where the user passes in an appropriate comparator that can be used to sort whatever type things end up in __args.
However, I'm relatively new to cpp, I don't understand half of the code in this function, and the std::sort needs an argument for the end of __args, and I'm not sure how to derive that.
Also please explain the typename __decay_and_strip<_Elements>::__type... and _Elements&&... bits...
EDIT Since for arbitrary type combinations the return type would then be unknown at compile time, the generic case seems to be not possible. Supposing all the same type, then, and we replace ..._Elements with T, I'm still unsure how to get the ".end()" of __args for use in std::sort
This can be done if the tuple type arguments are homogeneous. (We can't sort non-homogeneous types because that would require rearrangement of the types themselves, and that's not something you can do at compile time.1)
Assuming homogeneous types, the solution basically boils down to this:
Throw the arguments into an array.
Sort the array.
Make a tuple from the array contents.
This isn't too hard. First we need the indices trick to index our array (for step 3 -- you can use std::index_sequence instead if you have C++14):
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
Then we need a way to peel off the first type from the parameter pack to declare our array (for step 1). As a bonus, we'll have it check to make sure all the types are the same:
template <typename...>
struct pack_type;
template <typename Head>
struct pack_type<Head>
{
using type = Head;
};
// Will fail deduction on a non-homogeneous pack.
template <typename Head, typename... Tail>
struct pack_type<Head, Head, Tail...> : pack_type<Head, Tail...> {};
Finally, our sorter implementation with a helper to build the indices pack:
template <std::size_t... I, typename Comparer, typename... Ts>
std::tuple<Ts...> make_sorted_tuple_impl(indices<I...>, Comparer const &c, Ts && ...args)
{
typename pack_type<Ts...>::type values[sizeof...(Ts)] = { std::forward<Ts>(args)... };
std::sort(std::begin(values), std::end(values), c);
return std::make_tuple(std::forward<Ts>(values[I])...);
}
// Special case to handle empty tuples.
template <typename Comparer>
std::tuple<> make_sorted_tuple_impl(indices<>, Comparer const &)
{
return std::tuple<>();
}
template <typename Comparer, typename... Ts>
std::tuple<Ts...> make_sorted_tuple(Comparer const &c, Ts && ...args)
{
return make_sorted_tuple_impl(build_indices<sizeof...(Ts)>(), c, std::forward<Ts>(args)...);
}
See it run.
Also please explain the typename __decay_and_strip<_Elements>::__type... and _Elements&&... bits...
I'm not going to explain the first because identifiers containing __ are reserved by the C++ implementation, so this __decay_and_strip is an implementation detail specific to this particular C++ implementation.
_Elements&&... is a pack of rvalue references. This allows the arguments to be perfectly forwarded to the std::tuple constructor.
1 I lied. You can do it if the values and the compare function are constexpr, but the code to pull it off will be huge and not worth the time to write.
Variadic templates are really useful for doing recursive operations. In this case I'd like each recursive call to operate on two arguments so I don't have to repeatedly call the same function. To achieve this I can write:
f() {}
template<typename M,
typename N,
typename... Rest>
f(M arg1, N arg2, Rest... rest)
{
doStuff(arg1, arg2);
f(rest);
}
Then I would call this as such:
f(arg1a, arg1b,
arg2a, arg2b,
arg3a, arg3b);
However, if the call is not formatted so nicely, and all the arguments are on one line, or the column is split at the wrong point, it becomes quite unreadable. Especially so if the call might contain a dozen or so pairs. I attempted to remedy this by requiring a parameter pack of pairs to be passed in. I would like the function to have to be called like this:
f({arg1a, arg1b},
{arg2a, arg2b},
{arg3a, arg3b});
This seems to be mostly failing as the initializer list does not get deduced to a pair. I can call make_pair on each set of arguments, but that just solves a readability problem with another readability problem. Is there any way to get this sort of calling syntax to work? The arguments are not the same across pairs or within pairs.
Sorry, it is not possible: an implicit type conversion from braced list to a user-defined type. And the only conversion that is possible is for std::initializer_list, but it can be only done for the same types, or for convertible types.
With this said, I would offer this, as a possibility,
template <typename M, typename N>
void doStuff(M&&, N&&) {}
template <typename M, typename N>
struct MyRepeater;
template <typename M, typename N>
MyRepeater<M, N> f(M&& one, N&& two);
template <typename M, typename N>
struct MyRepeater {
template <typename I, typename J>
MyRepeater<I, J> operator()(I&& one, J&& two) const {
return f(std::forward<I>(one), std::forward<J>(two));
}
};
template <typename M, typename N>
MyRepeater<M, N> f(M&& one, N&& two) {
doStuff(one, two);
return MyRepeater<M, N>();
}
int main() {
f(Foo1(), Foo2())(Bar1(), Bar2())(Bar2(), Foo1());
}
Of course, the downside is that only for readability you have two write some extra code (in my opinion, it is not a bad motivation).
It may seem surprising but the best solution I've found for this is actually a "blast from the past" that doesn't use variadics at all.
struct F {
template <class M, class N>
F& operator()(M arg1, N arg2) {
doStuff(arg1, arg2);
return *this;
}
};
F{} (arg1a, arg1b)
(arg2a, arg2b);
And so on. Although, I'll also note that clang-format does not format this nicely. So I ended up doing something slightly different again.
I'm writing a container storage class template that wraps a private std::array in order to add some functionality to it. The template parametrises the number of values, as follows:
template<size_t N> class Vector {
private:
array<double, N> vals;
public:
[...]
};
I'd like the constructor for the class to only accept N doubles to fill the array, but I can't find a good way to do this. Variadic arguments don't provide a mechanism to check how many of them there are, so they're right out. Parameter packs don't do floating-point promotion, but I'd be willing to deal with that if I could only figure out how to use them for this.
I've tried following the approach in the answer to Member function template with the number of parameters depending on an integral template parameter but I can't understand the significance enable_if<>::type=0 section. I've tried naïvely copying that code in (though I'd much rather understand how it works. I've seen people use ::value in other places but I can't find any documentation on why) but expanding the resulting parameter pack doesn't seem to work. My other concern with parameter packs is that I'm not sure that they'd ensure the types of all arguments were the same.
I've tried running a static_assert on the size of an initializer list, in the body of the constructor, but of course the size of the list is not constant at compile time, so that doesn't work either.
Is there a standard approach here? Am I just using parameter packs wrong?
Update:
I've got the approach in the answer I linked above partly working:
template<size_t N> class Vector {
private:
array<double, N> vals;
public:
template <typename ...T,
typename enable_if<sizeof...(T) == N, int>::type = 0>
Vector(T ...args) {
vals = {args...};
}
};
The issue is now that the enable_if term in the template means that when I initialise a Vector with, for example,
Vector<3> V {1.0, 2.0, 3.0};
It requests a template specialisation Vector<3>::Vector<double, double, double, 0> rather than <double, double, double>. How do I get rid of this stray term in the template?
Don't get what you mean by this:
Variadic arguments don't provide a mechanism to check how many of
them there are, so they're right out
template <typename ...T>
Vector(T... args) {
static_assert(sizeof...(args) <= N, "oops");
}
Should work..
You could additionally generate a pack of the right size via some template specialization tricks:
template <size_t N, class = std::make_index_sequence<N>>
class Vector;
template <size_t N, size_t... Is>
class Vector<N, std::index_sequence<Is...>>
{
private:
std::array<double, N> vals;
template <size_t >
using double_ = double;
public:
Vector(double_<Is>... vals)
{
...
}
};
That is a non-template constructor which takes N doubles.
By following Member function template with the number of parameters depending on an integral template parameter and Variadic templates with exactly n parameters, have got it working with the following code.
template<size_t N> class Vector {
private:
array<double, N> vals;
public:
template <typename ...T,
typename enable_if<sizeof...(T) == N, int>::type = 0>
Vector(T ...args) : vals{args} {}
};
I want to write a constexpr function, that reduces a given std::array with a binary operation. I.e. a function which implements
template <typename T, std::size_t N>
reduce(std::array<T, N>, binary_function);
To keep things simple I want to start with addition. E.g.
sum(std::array<int, 5>{{1,2,3,4,5}}); // returns 15.
What I got so far.
I use the usual indexing trick to index array elements. I.e. generate a int sequence, that can be used for indexing with parameter list-expansion.
template <int... Is>
struct seq {};
template <int I, int... Is>
struct gen_seq : gen_seq<I - 1, I - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : seq<Is...> {}; // gen_seq<4> --> seq<0, 1, 2, 3>
The sum function is then defined through variadic template recursion.
// The edge-condition: array of one element.
template <typename T>
constexpr T sum(std::array<T, 1> arr, decltype(gen_seq<0>{})) {
return std::get<0>(arr);
}
// The recursion.
template <typename T, std::size_t N, int... Is>
constexpr auto sum(std::array<T, N> arr, seq<Is...>) -> decltype(T() + T()) {
return sum(std::array<T, N - 1>{ { std::get<Is>(arr)... } },
gen_seq<N - 2>()) +
std::get<N - 1>(arr);
}
// The interface - hides the indexing trick.
template <typename T, std::size_t N>
constexpr auto sum(std::array<T, N> arr)
-> decltype(sum(arr, gen_seq<N - 1>{})) {
return sum(arr, gen_seq<N - 1>{});
}
Here you can see it in action.
Questions
This implementation works. However, I do have a few questions at this stage.
Is there any way, I can add perfect-forward to this function? And does that even make sense? Or should I declare those arrays const-references?
The assumption so far is, that the return-type of the reduction is decltype(T()+T()). I.e. what you get when you add two elements. While this should be true for addition in most cases, it might no longer be true for a general reduction. Is there a way, of getting the type of a[0] + (a[1] + (a[2] + ... ) )? I tried something like this, but I don't know how I can produce a template parameter list of <T, T, T, ...>.
My answer is based on my own implementation of such staff.
I prefer the general reduce (or fold, or accumulate) function to operate directly on elements as its own function arguments rather than being within a container like std::array. This way, instead of constructing a new array in every recursion, elements are passed as arguments and I guess the whole operation is easier for the compiler to inline. Plus it's more flexible, e.g. could be used directly or on the elements of a std::tuple. The general code is here. I repeat here the main function:
template <typename F>
struct val_fold
{
// base case: one argument
template <typename A>
INLINE constexpr copy <A>
operator()(A&& a) const { return fwd<A>(a); }
// general recursion
template <typename A, typename... An>
INLINE constexpr copy <common <A, An...> >
operator()(A&& a, An&&... an) const
{
return F()(fwd<A>(a), operator()(fwd<An>(an)...));
}
};
I am sorry this is full of my own definitions, so here is some help: F is the function object defining the binary operation. copy is my generalization of std::decay that recurses within arrays and tuples. fwd is just a shortcut for std::forward. Similarly, common is just a shortcut for std::common_type but intended for a similar generalization (in general, each operation may yield an expression template for lazy evaluation and here we are forcing evaluation).
How would you define sum using the above? First define the function object,
struct sum_fun
{
template <typename A, typename B>
INLINE constexpr copy <common <A, B> >
operator()(A&& a, B&& b) const { return fwd<A>(a) + fwd<B>(b); }
};
then just
using val_sum = val_fold<sum_fun>;
How would you call this when starting with an std::array? Well, once you've got your Is..., all you need is
val_sum()(std::get<Is>(arr)...);
which you may wrap within your own interface. Note that in C++14, std::array::operator[] is constexpr, so this would just be
val_sum()(arr[Is]...);
Now, to your questions:
1) Forwarding: Yes, std::get is forwarding array elements into val_sum, which is recursively forwarding everything to itself. So all that remains is your own interface to forward the input array, e.g.
template <typename A, /* enable_if to only allow arrays here */>
constexpr auto sum(A&& a) -> /* return type here */
{
return sum(std::forward<A>(a), gen_seq_array<A>{});
}
and so on, where gen_seq_array would take the raw type of A (std::remove_ref, std::remove_cv etc.), deduce N, and call gen_seq<N>{}. Forwarding makes sense if array elements have move semantics. It should be everywhere, e.g. the call of val_sum above would be something like
val_sum()(std::get<Is>(std::forward<A>(a))...);
2) Return type: As you have seen, I am using std::common_type as the return type, which should do for sum and most common arithmetic operations. This is already variadic. If you'd like your own type function, it's easy to make a variadic out of a binary type function, using template recursion.
My own version of common is here. It's a bit more involved, but it is still a recursive template containing some decltype to do the actual work.
In any case, this is more general than what you need, because it's defined for an arbitrary number of any given types. You only have one type T in your array, so what you have should be enough.
So I'm trying to figure out how this works: C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?
The piece of black magic I do not understand is this code fragment:
f(std::get<N>(std::forward<Tuple>(t))...)
it's the expression inside f that I don't understand.
I understand that the expression somehow unpacks/expands what's inside t into a list of arguments. But could someone care to explain how this is done? When I look at the definition of std::get (http://en.cppreference.com/w/cpp/utility/tuple/get), I don't see how N fits in...? As far as I can tell, N is a sequence of integers.
Based on what I can observe, I'm assuming that expressions in the form E<X>... where X is the sequence of types X1. X2, ... Xn, the expression will be expanded as E<X1>, E<X2> ... E<Xn>. Is this how it works?
Edit: In this case N is not a sequence of types, but integers. But I'm guessing this language construct applies to both types and values.
I think that #Xeo's comment summed it up well. From 14.5.3 of the C++11 standard:
A pack expansion consists of a pattern and an ellipsis, the
instantiation of which produces zero or more instantiations of the
pattern in a list.
In your case, by the time you finish with the recursive template instantiation and end up in the partial specialization, you have
f(std::get<N>(std::forward<Tuple>(t))...);
...where N is parameter pack of four ints (0, 1, 2, and 3). From the standardese above, the pattern here is
std::get<N>(std::forward<Tuple>(t))
The application of the ... ellipsis to the above pattern causes it to be expanded into four instantiations in list form, i.e.
f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
The fundamental ingredient to expanding the std::tuple<T...> is actually omitted from the code: you need to obtain a a second parameter back: in addition to the list of types of the std::tuple<...> you need a parameter pack with indices 0, 1, ..., n. Once you have these two parameters packs, you can expand them in tandem:
template <typename F, typename... T, int... N>
void call_impl(F&& fun, std::tuple<T...>&& t) {
fun(std::get<N>(t)...);
}
The real magic lies in conjuring up the second parameter pack when you just have a std::tuple<T...>. It takes a bit of template programming. Here is an approach to create the list of indices:
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;
}
So, if you have a function template, let's call it call() which takes a function object and a std::tuple<T...> with the arguments to the function. An easy approach is to rewrite the call_impl() mentioned above to deal with deducing the indices:
template <typename F, typename Tuple, int... N>
void call_impl(F&& fun, Tuple&& t, indices<Indices...> const*)
{
fun(std::get<N>(t)...);
}
template <typename F, typename Tuple>
void call(F&& fun, Tuple&& t)
{
call_imle(std::forward<F>(fun), std::forward<Tuple>(t), make_indices<Tuple>());
}
What this code doesn't really extend is the correct use of std::forward<...>() with the various std::tuple<...> elements when calling the function. Just using std::forward<Tuple>(t) does not work because it possibly moves the entire std::tuple<...> rather than moving the elements. I think something like a suitable element-wise move of a std::tuple<...> can be done but I haven't done it, yet.