问题描述
我试图了解如何使用函数、输入参数和输出参数类型创建和调用可变参数模板。我写了这个玩具示例:
#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;
}
但是,它不会编译。我应该如何修改它以获得 13 结果?
我收到此错误消息:
<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>
解决方法
你可以把这个模板函数写成:
template<typename Func,typename... Inputs>
auto foo(int init,Func&& func,Inputs&&... args) {
return std::forward<Func>(func)(init,std::forward<Inputs>(args)...);
}
原始版本的问题在于typename... Outputs
,无法推断。您需要明确指定它们 - 但这是不可能的,因为该模板中有两个可变参数包 - 因此无法说明 Inputs... 在哪里结束以及 Outputs 在哪里开始。
或者 - 您可以只指定一个结果类型名称 - 并指定该类型:
template<typename Output,typename Func,typename... Inputs>
Output foo(int init,std::forward<Inputs>(args)...);
}
并调用:
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
);
或者将函数模板作为静态函数移动到类模板中:
template<typename ...Output>
struct Foo
{
template <typename Func,typename... Inputs>
static std::tuple<Output...> foo(int init,std::forward<Inputs>(args)...);
}
};
并调用:
auto bar = Foo<double,double>::foo(
init,mult
);
,
foo
的主体不可用于推断 Outputs...
,因此它们被推断为空。
要强制它返回第一个元素为 std::tuple
的 double
,您可以编写一个特征。
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,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)...);
};