I'm trying to understand how to create and call variadic templates with function, inpus args and output args types. I wrote this toy example:
#include <tuple>
template<typename Func, typename... Inputs, typename... Outputs>
std::tuple<double, Outputs...> foo(int init, Func&& func, Inputs&&... args) {
return std::forward<Func>(func)(init, std::forward<Inputs>(args)...);
};
int main () {
int init = 6;
double mult = 2.3;
std::tuple<double, double> bar = foo(
init,
[](int init_, double mult_) {
double res = init_ * mult_;
return std::make_tuple(res, 4.1);
},
mult
);
int out = std::get<0>(bar);
return out;
}
However, it doesn't compile. How should I modify it to get 13 as result?
I get this error message:
<source>: In function 'int main()':
<source>:11:41: error: conversion from 'tuple<double>' to non-scalar type 'tuple<double, double>' requested
11 | std::tuple<double, double> bar = foo(
| ~~~^
12 | init,
| ~~~~~
13 | [](int init_, double mult_) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 | double res = init_ * mult_;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
15 | return std::make_tuple(res, 4.1);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 | },
| ~~
17 | mult
| ~~~~
18 | );
| ~
<source>: In instantiation of 'std::tuple<double, Outputs ...> foo(int, Func&&, Inputs&& ...) [with Func = main()::<lambda(int, double)>; Inputs = {double&}; Outputs = {}]':
<source>:18:5: required from here
<source>:5:72: error: could not convert 'main()::<lambda(int, double)>(init, std::forward<double&>((* & args#0)))' from 'tuple<double, double>' to 'tuple<double>'
5 | return std::forward<Func>(func)(init, std::forward<Inputs>(args)...);
| ^
| |
| tuple<double, double>
You can write this template function as:
template<typename Func, typename... Inputs>
auto foo(int init, Func&& func, Inputs&&... args) {
return std::forward<Func>(func)(init, std::forward<Inputs>(args)...);
}
The problem with the original version is with typename... Outputs
, they cannot be deduced. You would need to specify them explicitly - but this is impossible because there are two variadic packs in that template - so it is impossible to say where Inputs... ends and where Outputs starts.
Alternatively - you can just specify one result typename - and specify that type:
template<typename Output, typename Func, typename... Inputs>
Output foo(int init, Func&& func, Inputs&&... args) {
return std::forward<Func>(func)(init, std::forward<Inputs>(args)...);
}
and call:
std::tuple<double, double> bar = foo<std::tuple<double, double>>(
init,
[](int init_, double mult_) {
double res = init_ * mult_;
return std::make_tuple(res, 4.1);
},
mult
);
Or move the function template into class template as static function:
template<typename ...Output>
struct Foo
{
template <typename Func, typename... Inputs>
static std::tuple<Output...> foo(int init, Func&& func, Inputs&&... args) {
return std::forward<Func>(func)(init, std::forward<Inputs>(args)...);
}
};
and call:
auto bar = Foo<double, double>::foo(
init,
[](int init_, double mult_) {
double res = init_ * mult_;
return std::make_tuple(res, 4.1);
},
mult
);
The body of foo
isn't available to deduce Outputs...
, so they are deduced as empty.
To enforce that it returns a std::tuple
where the first element is double
, you can write a trait.
template <typename>
struct is_tuple_of_double : std::false_type {};
template <typename... Others>
struct is_tuple_of_double<std::tuple<double, Others...>> : std::true_type {};
template <typename T>
constexpr bool is_tuple_of_double_v = is_tuple_of_double<T>::value;
template<typename Func, typename... Inputs>
auto foo(int init, Func&& func, Inputs&&... args) {
static_assert(is_tuple_of_double_v<decltype(std::forward<Func>(func)(init, std::forward<Inputs>(args)...)>);
return std::forward<Func>(func)(init, std::forward<Inputs>(args)...);
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.