Can I default initialize a parameter pack to the respective value initialization of each type ?
To elaborate a bit more, take the example of a simple function template
template<typename T>
void f(T arg = T())
{
// eg for T=int, arg is 0 (value initialization) when default initialized
}
Would it be possible to express its variadic counterpart, ie
template<typename... Args>
void F(Args... args /* how can I value initialize the parameter pack? */)
{
}
#include <iostream>
#include <utility>
#include <tuple>
#include <cstddef>
#include <type_traits>
template <typename... Args>
void F(Args... args)
{
// target function, arbitrary body
using expander = int[];
(void)expander{ 0, (void(std::cout << args << " "), 0)... };
std::cout << std::endl;
}
template <typename... Args, typename... Params, std::size_t... Is>
void F(std::index_sequence<Is...>, Params&&... params)
{
F<Args...>(std::forward<Params>(params)...
, std::decay_t<typename std::tuple_element<sizeof...(Params) + Is, std::tuple<Args...>>::type>{}...);
}
template <typename... Args, typename... Params>
auto F(Params&&... params)
-> std::enable_if_t<(sizeof...(Args) > sizeof...(Params))>
{
F<Args...>(std::make_index_sequence<sizeof...(Args) - sizeof...(Params)>{}
, std::forward<Params>(params)...);
}
Tests:
#include <string>
int main()
{
// F(int, char, float = float{}, double = double{})
F<int, char, float, double>(1, 'c');
// F(int = int{}, char = char{}, float = float{}, double = double{})
F<int, char, float, double>();
// F(const std::string&, const std::string& = std::string{})
F<const std::string&, const std::string&>("foo");
// F(int, int, int)
F(1, 2, 3);
}
Output:
1 'c' 0 0
0 '\0' 0 0
"foo" ""
1 2 3
DEMO
You can create two parameter packs, one representing the types corresponding to function parameters and one representing "defaulted parameters."
template< typename ... aux, typename ... arg >
void fn( arg ... a ) {
std::tuple< aux ... > more {}; // The tuple elements are value-initialized.
}
http://coliru.stacked-crooked.com/a/1baac4b877dce4eb
There is no way to explicitly mention the deduced template parameters for this function. Anything inside the angle braces of the call will go into aux, not arg.
Note, the initialization you get with {} is value-initialization, not default-initialization. Objects of fundamental type get zeroed, not left uninitialized.
It`s explicitly forbidden by C++ standard, you cannot do such thing.
N3376 8.3.6/3
A default argument shall be specified only in the
parameter-declaration-clause of a function declaration or in a
template-parameter (14.1); in the latter case, the initializer-clause
shall be an assignment-expression. A default argument shall not be
specified for a parameter pack.
Related
I am trying to have a kind of "invoke" function with an optional argument at the end:
template <typename... T>
void foo(void func(T...), T... args, int opt = 0)
{
func(args...);
}
void bar(int, int);
int main()
{
foo(&bar, 1, 2, 3);
}
I would have expected this to work, since the parameter pack can be deduced from the first argument, but clearly the compilers have different ideas:
Clang for example gives:
<source>:11:5: error: no matching function for call to 'foo'
foo(&bar, 1, 2, 3);
^~~
<source>:2:6: note: candidate template ignored: deduced packs of different lengths for parameter 'T' (<int, int> vs. <>)
void foo(void func(T...), T... args, int opt = 0)
^
1 errors generated.
Compiler returned: 1
Why is it deducing a list of length 0? Can I force it to ignore args for the purposes of deduction? Or more generally, how can I make this work?
You could make it overloaded instead of having an optional argument. You'd need to move the "optional" to before the parameter pack though.
The second overload would then just forward the arguments to the first, with the "default" parameter set.
#include <iostream>
template <typename... T>
void foo(void(func)(T...), int opt, T... args)
{
std::cout << opt << '\n';
func(args...);
}
template <typename... T>
void foo(void(func)(T...), T... args)
{
return foo(func, 0, args...); // forwards with the default set
}
void bar(int, int) {}
int main()
{
foo(&bar, 1, 2); // prints 0
foo(&bar, 3, 1, 2); // prints 3
}
You might want to move the optional all the way to the first position to let the function and its parameters be together. It's a matter of taste.
Another option could be to exclude the optional parameter and only have the parameter pack and to extract the optional if it's present or use the default value if it's not.
This requires that you restrict the signature of func to match the function you aim to call.
#include <iostream>
#include <tuple>
template <class... T>
void foo(void func(int, int), T&&... args) {
int opt = [](T... args) {
if constexpr (sizeof...(T) > 2) return std::get<2>(std::tuple{args...});
else return 0; // use the default
}(args...);
std::cout << opt << '\n';
[&func](int a, int b, auto&&...) { func(a, b); }(args...);
}
void bar(int, int) {}
int main() {
foo(&bar, 1, 2); // prints 0
foo(&bar, 1, 2, 3); // prints 3
}
Building on the second version but giving a lot more freedom, you could introduce a separate parameter pack for func. If that pack has the same size as pack of arguments supplied, you need to pick a default value for opt. If it on the other hand contains more arguments than needed for the function, you can select which one of the extra arguments that should be used for opt. In the example below, I just picked the first extra parameter.
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
// a helper to split a tuple in two:
template <class... T, size_t... L, size_t... R>
auto split_tuple(std::tuple<T...> t,
std::index_sequence<L...>,
std::index_sequence<R...>)
{
return std::pair{
std::forward_as_tuple(std::get<L>(t)...),
std::forward_as_tuple(std::get<R+sizeof...(L)>(t)...)
};
}
template <class... A, class... T>
void foo(void func(A...), T&&... args) {
static_assert(sizeof...(T) >= sizeof...(A));
// separate the needed function arguments from the rest:
auto[func_args, rest] =
split_tuple(std::forward_as_tuple(std::forward<T>(args)...),
std::make_index_sequence<sizeof...(A)>{},
std::make_index_sequence<sizeof...(T)-sizeof...(A)>{});
int opt = [](auto&& rest) {
// if `rest` contains anything, pick the first one for `opt`
if constexpr(sizeof...(T) > sizeof...(A)) return std::get<0>(rest);
else return 0; // otherwise return a default value
}(rest);
std::cout << opt << '\n';
std::apply(func, func_args);
}
void bar(int a, int b) {
std::cout << a << ',' << b << '\n';
}
int main() {
foo(&bar, 1, 2); // prints 0 then 1,2
foo(&bar, 1, 2, 3, 4); // prints 3 then 1,2
}
how can I make this work?
You can put the function arguments in a std::tuple, to make them distinct from your optional parameter.
C++17 provides std::apply to unpack the tuple parameters for you.
#include <tuple>
template <typename... T>
void foo(void func(T...), std::tuple<T...> args, int opt = 0)
{
std::apply( func, args );
}
void bar(int, int);
int main()
{
foo(&bar, {1, 2}, 3);
// args ^^^^^^
}
I invoke a templated lambda from a templated function, the lambda parameters type are deduced. If the type of the lambda if auto, it works :
https://godbolt.org/z/WYxj5G8vx
#include <iostream>
#include <cstdint>
#include <array>
#include <functional>
#include <numeric>
#include <concepts>
template <typename T>
int testf2(T, auto fun) {
std::array<std::uint8_t, sizeof(T)> ar{};
std::iota(ar.begin(), ar.end(), 0);
return fun(ar);
}
int main() {
auto f2 = []<size_t S> (std::array<uint8_t, S> arr) -> int {
return arr[S -1];
};
std::cout << "R = " << testf2(5, f2) << std::endl;
}
I wanted to use std::invocable concept to specialize the auto fun parameter of testf2, to be anything but a callable that take std::array<std::uint8_t, N> as parameter.
Using gcc11.2 or clang13, when I try
template <typename T, size_t S>
int testf2(T, std::invocable<std::array<uint8_t, S>> auto fun) {
std::array<std::uint8_t, sizeof(T)> ar{};
std::iota(ar.begin(), ar.end(), 0);
return fun(ar);
}
I get error :
candidate template ignored: couldn't infer
template argument 'S' int testf2(T, std::invocable<std::array<uint8_t,
S>> auto fun) {
I don't understand why the compiler can infer type when only auto is used, but not with a constraining concept.
What is the correct way to use concept in this situation ?
This is a simplified version of the code, in reality the signature of testf2 is testf2(auto fun, ARGS... args) and the size of the array is calculated upon the parameter pack types.
============ EDIT 03/03/2022 ==================
Thanks for the correct answers, but I have oversimplified the code and the question, so I get right answer to a wrong question.
You need more context, I work with MCUs, and want to make a function that abstract some kind of spi,i2c,modbus, etc transaction where one send buffer to the slave peripheral and receive buffer in response. The function calculate write and read buffer length, serialise (doing endianness conversion if needed), call a lambda to do the actual transaction depending on the transport mechanism, deserialise and return. So the buffers lengths cannot be calculated with a (sizeof(Ts) + ...) as suggested.
I made a more realistic example :live example
// return empty array whose size is the sum of the two arrays given as parameters
template<typename T, std::size_t LL, std::size_t RL>
constexpr std::array<T, LL+RL> join(std::array<T, LL>, std::array<T, RL>)
{
return std::array<T, LL+RL>{};
}
// return an array of size sizeof(T) if T is arithmetic, otherwise an empty array
template <typename T>
constexpr auto count_ari(T) {
if constexpr (std::is_arithmetic_v<T>) {
return std::array<uint8_t, sizeof(T)>{};
} else {
return std::array<uint8_t, 0>{};
}
}
// return empty array whose size is the sum of all parameter which are arithmetic
template <typename HEAD, typename... TAIL>
constexpr auto count_ari(HEAD h, TAIL... tail) {
return join(count_ari(h), count_ari(tail...));
}
// create a iota filled array whose size is sum of all arithmetic parameters
// call a lambda given in parameter on this array
// return what has done the lambda
// it's here that I want to constrain parameter "auto fun"
template </*size_t S,*/ typename... ARGS>
int testf2(/*std::invocable<std::array<uint8_t, S>>, */ auto fun, ARGS... args) {
auto ar = count_ari(args...);
std::iota(ar.begin(), ar.end(), 1);
return fun(ar);
}
int main() {
auto f2 = []<size_t S> (std::array<uint8_t, S> arr) -> int {
return arr[S -1];
};
std::cout << "R = " << testf2(f2, 'a') << std::endl;
std::cout << "R = " << testf2(f2, 6, 7l, "foobar") << std::endl;
}
Question remains the same : is there a way to add constrain on the auto fun parameter of function testf2
Concepts (and requires clauses in general) do not participate in template argument deduction. Since your S in this case is just sizeof(T), you should use that.
the size S is the sum of all the sizes of the types of a parameter pack
Then make it sizeof(Args) + ....
You can use delcltype(count_ari(args...)) to get the resulting array type as the template parameter of std::invocable:
template <typename... ARGS>
int testf2(
std::invocable<decltype(count_ari(std::declval<ARGS>()...))> auto fun,
ARGS... args);
Or
template <typename... ARGS>
int testf2(auto fun, ARGS... args)
requires std::invocable<decltype(fun), decltype(count_ari(args...))>;
Demo
Nicol Bolas helps me to find the solution which was to make a constexpr function taking no argument that calculate the size, and specify the exact type of the callable with std::function instead of trying to specialise auto with invocable concept.
template <typename T>
constexpr size_t sizeof_ari() {
if constexpr (std::is_arithmetic_v<T>)
return sizeof(T);
else
return 0;
}
template <typename... ARGS>
constexpr size_t sizeof_aris() {
return (sizeof_ari<ARGS>() + ...);
}
// create a iota filled array whose size is sum of all arithmetic parameters
// call a lambda given in parameter on this array
// return what has done the lambda
template <typename... ARGS>
using lambda_param = std::array<uint8_t, sizeof_aris<ARGS...>()>;
template <typename... ARGS>
int testf2(std::function<int(lambda_param<ARGS...>)> fun, ARGS... args) {
auto ar = make_buf(args...);
std::iota(ar.begin(), ar.end(), 1);
return fun(ar);
}
demo
Take the following code, which is a simplified example:
template <typename F>
void foo(F f) {
//bool some = is_variadic_v<F>; // Scenario #1
bool some = true; // Scenario #2
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
A function takes a generic variadic lambda and calls it with a fixed set of arguments. This lambda itself then just calls another function/lambda with a matching prototype.
As one could expect, in scenario 2, when f is called inside foo, the compiler will deduce params... to be the parameter pack {1, 1}.
For scenario #1, I am using a code from another Q&A to deduce the arity of a callable object. If however such an object is callable with more than a pre-defined maximum amount of arguments, it is considered "variadic". In detail, is_variadic_v will employ a form of expression SFINAE where it is attempted to call the function object with a decreasing number of arguments having an "arbitrary type" that is implictly convertible to anything.
The problem is now that apparently, the compiler will deduce F (and along its argument pack) during this metacode, and if it is variadic (such as in this case), it deduces F as a lambda taking the dummy arguments, i.e. something like main()::lambda(<arbitrary_type<0>, arbitrary_type<1>, arbitrary_type<2>, ..., arbitrary_type<N>>) if N is the "variadic limit" from above. Now params... is deduced as arbitrary_type<1>, arbitrary_type<2>, ... and correspondingly, the call some(params...) will fail.
This behaviour can be demonstrated in this little code example:
#include <utility>
#include <type_traits>
#include <iostream>
constexpr int max_arity = 12; // if a function takes more arguments than that, it will be considered variadic
struct variadic_type { };
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hence, to 'simulate' an arbitrary function signature.
template <auto>
struct arbitrary_type {
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&&();
template <typename T>
operator T&();
};
template <
typename F, auto ...Ints,
typename = decltype(std::declval<F>()(arbitrary_type<Ints>{ }...))
>
constexpr auto test_signature(std::index_sequence<Ints...> s) {
return std::integral_constant<int, size(s)>{ };
}
template <auto I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{ })) {
return { };
}
template <auto I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...) {
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, auto MaxArity>
constexpr auto arity_impl() {
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity+1, F>(0);
if constexpr (tmp == MaxArity+1)
return variadic_type{ }; // if that works, F is considered variadic
else return tmp; // if not, tmp will be the correct arity of F
}
template <typename F, auto MaxArity = max_arity>
constexpr auto arity(F&&) { return arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, auto MaxArity = max_arity>
constexpr auto arity_v = arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, auto MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_type>;
template <typename F>
void foo(F f) {
bool some = is_variadic_v<F>;
//bool some = true;
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
Can I prevent this behaviour? Can I force the compiler to re-deduce the parameter list?
EDIT:
An additional peculiarity is that the compiler seems to act kind of schizophrenic. When I change the contents of foo to
foo([&some](auto... params) {
// int foo = std::index_sequence<sizeof...(params)>{ };
std::cout << sizeof...(params) << '\n';
});
the compiler will create a program that will print 2 in this example. If however I include the commented line (which, as it makes no sense, should trigger a compiler diagnostic), I get confronted with
error: cannot convert 'std::index_sequence<13>' {aka 'std::integer_sequence<long unsigned int, 13>'} to 'int' in initialization
85 | int foo = std::index_sequence<sizeof...(params)>{ };
so does the compiler now deduces sizeof...(params) to be 2 and 13 at the same time? Or did he change his mind and chooses now 13 just because I added another statement into the lambda? Compilation will also fail if I instead choose a static_assert(2 == sizeof...(params));. So the compiler deduces sizeof...(params) == 2, except if I ask him whether he did deduce 2, because then he didn't.
Apparently, it is very decisive for the parameter pack deduction what is written inside the lambda. Is it just me or does this behaviour really look pathologic?
Given the following code(taken from here):
#include <cstddef>
#include <type_traits>
#include <tuple>
#include <iostream>
#include <utility>
#include <functional>
template<typename ... Fs>
struct compose_impl
{
compose_impl(Fs&& ... fs) : functionTuple(std::forward_as_tuple(fs ...)) {}
template<size_t N, typename ... Ts>
auto apply(std::integral_constant<size_t, N>, Ts&& ... ts) const
{
return apply(std::integral_constant<size_t, N - 1>(), std::get<N> (functionTuple)(std::forward<Ts>(ts)...));
}
template<typename ... Ts>
auto apply(std::integral_constant<size_t, 0>, Ts&& ... ts) const
{
return std::get<0>(functionTuple)(std::forward<Ts>(ts)...);
}
template<typename ... Ts>
auto operator()(Ts&& ... ts) const
{
return apply(std::integral_constant<size_t, sizeof ... (Fs) - 1>(), std::forward<Ts>(ts)...);
}
std::tuple<Fs ...> functionTuple;
};
template<typename ... Fs>
auto compose(Fs&& ... fs)
{
return compose_impl<Fs ...>(std::forward<Fs>(fs) ...);
}
int main ()
{
auto f1 = [](std::pair<double,double> p) {return p.first + p.second; };
auto f2 = [](double x) {return std::make_pair(x, x + 1.0); };
auto f3 = [](double x, double y) {return x*y; };
auto g = compose(f1, f2, f3);
std::cout << g(2.0, 3.0) << std::endl; //prints '13', evaluated as (2*3) + ((2*3)+1)
return 0;
}
The code above works in C++14. I'm having some trouble making it work for C++11. I tried to properly provide the return types for the function templates involved but without much success e.g.:
template<typename... Fs>
struct compose_impl
{
compose_impl(Fs&&... fs) : func_tup(std::forward_as_tuple(fs...)) {}
template<size_t N, typename... Ts>
auto apply(std::integral_constant<size_t, N>, Ts&&... ts) const -> decltype(std::declval<typename std::tuple_element<N, std::tuple<Fs...>>::type>()(std::forward<Ts>(ts)...))
// -- option 2. decltype(apply(std::integral_constant<size_t, N - 1>(), std::declval<typename std::tuple_element<N, std::tuple<Fs...>>::type>()(std::forward<Ts>(ts)...)))
{
return apply(std::integral_constant<size_t, N - 1>(), std::get<N>(func_tup)(std::forward<Ts>(ts)...));
}
using func_type = typename std::tuple_element<0, std::tuple<Fs...>>::type;
template<typename... Ts>
auto apply(std::integral_constant<size_t, 0>, Ts&&... ts) const -> decltype(std::declval<func_type>()(std::forward<Ts>(ts)...))
{
return std::get<0>(func_tup)(std::forward<Ts>(ts)...);
}
template<typename... Ts>
auto operator()(Ts&&... ts) const -> decltype(std::declval<func_type>()(std::forward<Ts>(ts)...))
// -- option 2. decltype(apply(std::integral_constant<size_t, sizeof...(Fs) - 1>(), std::forward<Ts>(ts)...))
{
return apply(std::integral_constant<size_t, sizeof...(Fs) - 1>(), std::forward<Ts>(ts)...);
}
std::tuple<Fs...> func_tup;
};
template<typename... Fs>
auto compose(Fs&&... fs) -> decltype(compose_impl<Fs...>(std::forward<Fs>(fs)...))
{
return compose_impl<Fs...>(std::forward<Fs>(fs)...);
}
For the above clang(3.5.0) gives me the following error:
func_compose.cpp:79:18: error: no matching function for call to object of type 'compose_impl<(lambda at func_compose.cpp:65:15) &, (lambda at func_compose.cpp:67:15) &,
(lambda at func_compose.cpp:68:15) &>'
std::cout << g(2.0, 3.0) << std::endl; //prints '13', evaluated as (2*3) + ((2*3)+1)
^
func_compose.cpp:31:10: note: candidate template ignored: substitution failure [with Ts = <double, double>]: no matching function for call to object of type
'(lambda at func_compose.cpp:65:15)'
auto operator()(Ts&&... ts) /*const*/ -> decltype(std::declval<func_type>()(std::forward<Ts>(ts)...))
^ ~~~
1 error generated.
If I try "option 2." I get pretty much the same error.
Apart from the fact that it looks very verbose I also cannot seem to get it right. Could anyone provide some insight in what am I doing wrong?
Is there any simpler way to provide the return types?
The error message for your first option is due to the fact that in
std::declval<func_type>()(std::forward<Ts>(ts)...)
you're trying to call the f1 functor with two arguments of type double (the ones passed to operator()), but it takes a std::pair (func_type refers to the type of the first functor in the tuple).
Regarding option 2, the reason it doesn't compile is that the trailing return type is part of the function declarator and the function is not considered declared until the end of the declarator has been seen, so you can't use decltype(apply(...)) in the trailing return type of the first declaration of apply.
I'm sure you're now very happy to know why your code doesn't compile, but I guess you'd be even happier if you had a working solution.
I think there's an essential fact that needs to be clarified first: all specializations of the apply and operator() templates in compose_impl have the same return type - the return type of the first functor, f1 in this case.
There are several ways to get that type, but a quick hack is the following:
#include <cstddef>
#include <type_traits>
#include <tuple>
#include <iostream>
#include <utility>
#include <functional>
template<typename> struct ret_hlp;
template<typename F, typename R, typename... Args> struct ret_hlp<R (F::*)(Args...) const>
{
using type = R;
};
template<typename F, typename R, typename... Args> struct ret_hlp<R (F::*)(Args...)>
{
using type = R;
};
template<typename ... Fs>
struct compose_impl
{
compose_impl(Fs&& ... fs) : functionTuple(std::forward_as_tuple(fs ...)) {}
using f1_type = typename std::remove_reference<typename std::tuple_element<0, std::tuple<Fs...>>::type>::type;
using ret_type = typename ret_hlp<decltype(&f1_type::operator())>::type;
template<size_t N, typename ... Ts>
ret_type apply(std::integral_constant<size_t, N>, Ts&& ... ts) const
{
return apply(std::integral_constant<size_t, N - 1>(), std::get<N> (functionTuple)(std::forward<Ts>(ts)...));
}
template<typename ... Ts>
ret_type apply(std::integral_constant<size_t, 0>, Ts&& ... ts) const
{
return std::get<0>(functionTuple)(std::forward<Ts>(ts)...);
}
template<typename ... Ts>
ret_type operator()(Ts&& ... ts) const
{
return apply(std::integral_constant<size_t, sizeof ... (Fs) - 1>(), std::forward<Ts>(ts)...);
}
std::tuple<Fs ...> functionTuple;
};
template<typename ... Fs>
compose_impl<Fs ...> compose(Fs&& ... fs)
{
return compose_impl<Fs ...>(std::forward<Fs>(fs) ...);
}
int main ()
{
auto f1 = [](std::pair<double,double> p) {return p.first + p.second; };
auto f2 = [](double x) {return std::make_pair(x, x + 1.0); };
auto f3 = [](double x, double y) {return x*y; };
auto g = compose(f1, f2, f3);
std::cout << g(2.0, 3.0) << std::endl; //prints '13', evaluated as (2*3) + ((2*3)+1)
return 0;
}
Notes:
It compiles and works on GCC 4.9.1 and Clang 3.5.0 in C++11 mode, and on Visual C++ 2013.
As written, ret_hlp only handles function object types that declare their operator() similarly to lambda closure types, but it can be easily extended to pretty much anything else, including plain function types.
I tried to change the original code as little as possible; I think there's one important bit that needs to be mentioned regarding that code: if compose is given lvalue arguments (as in this example), functionTuple inside compose_impl will store references to those arguments. This means the original functors need to be available for as long as the composite functor is used, otherwise you'll have dangling references.
EDIT: Here's more info on the last note, as requested in the comment:
That behaviour is due to the way forwarding references work - the Fs&& ... function parameters of compose. If you have a function parameter of the form F&& for which template argument deduction is being done (as it is here), and an argument of type A is given for that parameter, then:
if the argument expression is an rvalue, F is deduced as A, and, when substituted back into the function parameter, it gives A&& (for example, this would happen if you passed a lambda expression directly as the argument to compose);
if the argument expression is an lvalue, F is deduced as A&, and, when substituted back into the function parameter, it gives A& &&, which yields A& according to the reference collapsing rules (this is what happens in the current example, as f1 and the others are lvalues).
So, in the current example, compose_impl will be instantiated using the deduced template arguments as something like (using invented names for lambda closure types)
compose_impl<lambda_1_type&, lambda_2_type&, lambda_3_type&>
which in turn will make functionTuple have the type
std::tuple<lambda_1_type&, lambda_2_type&, lambda_3_type&>
If you'd pass the lambda expressions directly as arguments to compose, then, according to the above, functionTuple will have the type
std::tuple<lambda_1_type, lambda_2_type, lambda_3_type>
So, only in the latter case will the tuple store copies of the function objects, making the composed function object type self-contained.
Now, it's not a question of whether this is good or bad; it's rather a question of what you want.
If you want the composed object to always be self-contained (store copies of the functors), then you need to get rid of those references. One way to do it here is to use std::decay, as it does more than remove references - it also handles function-to-pointer conversions, which comes in handy if you want to extend compose_impl to be able to also handle plain functions.
The easiest way is to change the declaration of functionTuple, as it's the only place where you care about references in the current implementation:
std::tuple<typename std::decay<Fs>::type ...> functionTuple;
The result is that the function objects will always be copied or moved inside the tuple, so the resulting composed function object can be used even after the original components have been destructed.
Wow, this got long; maybe you shouldn't have said 'elaborate' :-).
EDIT 2 for the second comment from the OP: Yes, the code as it is, without the std::decay (but extended to properly determine ret_type for plain function arguments, as you said) will handle plain functions, but be careful:
int f(int) { return 7; }
int main()
{
auto c1 = compose(&f, &f); //Stores pointers to function f.
auto c2 = compose(f, f); //Stores references to function f.
auto pf = f; //pf has type int(*)(int), but is an lvalue, as opposed to &f, which is an rvalue.
auto c3 = compose(pf, pf); //Stores references to pointer pf.
std::cout << std::is_same<decltype(c1.functionTuple), std::tuple<int(*)(int), int(*)(int)>>::value << '\n';
std::cout << std::is_same<decltype(c2.functionTuple), std::tuple<int(&)(int), int(&)(int)>>::value << '\n';
std::cout << std::is_same<decltype(c3.functionTuple), std::tuple<int(*&)(int), int(*&)(int)>>::value << '\n';
}
The behaviour of c3 is probably not what you want or what one would expect. Not to mention all these variants will likely confuse your code for determining ret_type.
With the std::decay in place, all three variants store pointers to function f.
For mathematical purposes, I want to write a generic functor class to wrap simple mathematical equations with both variables and parameters. Imagine a simple parabola:
y=f(x;a,b,c)=a*x*x+b*x+c
Here a, b, and c are parameters set one time for one parabola. Then the
ordinate value y is computed by providing certain values of the
variable x. I've written a simple gen_func class using variadic
templates in combination with std::tuple for holding parameters and a
variadic parameter pack for variables. The skeleton for this class was
found at How to store variadic template arguments?
I am new to the new c++11 standard and I know that writing correct
code using variadic templates is a very complex thing.
For some purposes, I want to change the variable by call-by-reference. But here, the compiler (gcc 4.7) does complain. For simplicity, i tested the code with a simple function change. It's only purpose is to alter the value of the passed first variable. Here is the code of a simple program:
#include <tuple>
#include <iostream>
#include <functional>
#include<utility>
using namespace std;
// Compile with : g++ -o X.bin -Wall -pedantic -std=c++11 X.cpp
// Stuff for compile-time recursive parameter pack expansion:
namespace generic
{
template <std::size_t... Ts>
struct index_sequence {};
template <std::size_t N, std::size_t... Ts>
struct sequence_generator : sequence_generator<N - 1, N - 1, Ts...> {};
template <std::size_t... Ts>
struct sequence_generator<0, Ts...> : index_sequence<Ts...> {};
template< typename ReturnType , typename... Args > using generic_function_pointer = ReturnType (*) ( Args... args ) ;
}
// The generic Function wrapper:
template < typename R , class F , typename... Ps>
class gen_func
{
public:
// Constructor
gen_func(F&& f,Ps&&...parms){
func = forward<F>(f);
parameters=make_tuple(forward<Ps>(parms)...);
}
// execute-Function for public call:
template<typename... Vars>
R execute(Vars&&... vars) {
return f_decon<Vars... , Ps...>( forward<Vars>(vars)... , parameters );
}
//
private:
// "Deconvolution Function"
template<typename... Vars , typename... Ts >
R f_decon( Vars&&... vars , tuple<Ts...>& tup ){
return f_proxy<Vars...,Ps...>( forward<Vars>(vars)... , tup , generic::sequence_generator<sizeof...(Ts)>{} );
}
// "Proxy-Function" calling the wrapped Function func:
template<typename... Vars , typename... Ts , size_t... Is>
R f_proxy( Vars&&... vars , tuple<Ts...>& tup , generic::index_sequence<Is...> ){
return func( forward<Vars>(vars)... , get<Is>(tup)... );
}
// The wrapped Function f
F func;
// The tuple holding optional parameters for function f:
tuple<Ps...> parameters;
};
// Partial template Specialization of gen_func for Functions with Return Type void:
template <class F , typename... Ps>
class gen_func<void,F,Ps...>
{
public:
// Constructor
gen_func(F&& f,Ps&&...parms){
func = forward<F>(f);
parameters=make_tuple(forward<Ps>(parms)...);
}
// execute-Function for public call:
template<typename... Vars>
void execute(Vars&&... vars) {
f_decon<Vars... , Ps...>( forward<Vars>(vars)... , parameters ); // Line 75
}
//
private:
// The wrapped Function f
F func;
// The tuple holding optional parameters for function f:
tuple<Ps...> parameters;
// "Deconvolution Function"
template<typename... Vars , typename... Ts >
void f_decon( Vars&&... vars , tuple<Ts...>& tup ){ // Line 85
f_proxy<Vars...,Ts...>( forward<Vars>(vars)... , tup , generic::sequence_generator<sizeof...(Ts)>{} );
}
// "Proxy-Function" calling the wrapped Function func:
template<typename... Vars , typename... Ts , size_t... Is>
void f_proxy( Vars&&... vars , tuple<Ts...>& tup , generic::index_sequence<Is...> ){
func( forward<Vars>(vars)... , get<Is>(tup)... );
}
};
void change(int& m,int n){
cout << "Changing!" << endl;
int res=42+n;
m=res;
}
int main()
{
typedef generic::generic_function_pointer<void,int&,int> chg_ptr;
gen_func<void,chg_ptr> y(change);
// The Variable to be altered by change
int j=0;
cout << "j='" << j << "'" << endl; // should be 0
// Wrapped Function call:
y.execute(j,21); // Line 113
cout << "j='" << j << "'" << endl; // should be 63
return 0;
}
The compiler does complain with:
X.cpp: In instantiation of ‘void gen_func<void, F, Ps ...>::execute(Vars&& ...) [with Vars = {int&, int}; F = void (*)(int&, int); Ps = {}]’:
X.cpp:113:15: required from here
X.cpp:75:12: error: no matching function for call to ‘gen_func<void, void (*)(int&, int)>::f_decon(int&, int, std::tuple<>&)’
X.cpp:75:12: note: candidate is:
X.cpp:85:14: note: template<class ... Vars, class ... Ts> void gen_func<void, F, Ps ...>::f_decon(Vars&& ..., std::tuple<_Args2 ...>&) [with Vars = {Vars ...}; Ts = {Ts ...}; F = void (*)(int&, int); Ps = {}]
X.cpp:85:14: note: template argument deduction/substitution failed:
X.cpp:75:12: note: mismatched types ‘std::tuple<_Elements ...>’ and ‘int’
It seems, that the code is wrong by passing the variable x by reference so that it could be altered by wrapped function change. Is my program approach completely wrong? Is it simple to correct? Are there better ways to achieve my goal? Thanks in advance for any advice!