Luk*_*ang 6 c++ templates optional-parameters variadic-templates
我试图拥有一种最后带有可选参数的“调用”函数:
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);
}
Run Code Online (Sandbox Code Playgroud)
我本来希望这是可行的,因为参数包可以从第一个参数推导出来,但显然编译器有不同的想法:
<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
Run Code Online (Sandbox Code Playgroud)
为什么它会推导出长度为0的列表?args我可以为了扣除的目的而强制忽略它吗?或者更一般地说,我怎样才能做到这一点?
您可以使其重载,而不是使用可选参数。不过,您需要将“可选”移至参数包之前。
然后,第二个重载会将参数转发给第一个重载,并设置“默认”参数。
#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
}
Run Code Online (Sandbox Code Playgroud)
您可能希望将可选值一直移动到第一个位置,以使函数及其参数放在一起。这是一个品味问题。
另一种选择可能是排除可选参数,仅包含参数包,并提取可选参数(如果存在)或使用默认值(如果不存在)。这要求您限制 的签名func以匹配您要调用的函数。
#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
}
Run Code Online (Sandbox Code Playgroud)
在第二个版本的基础上构建,但提供了更多的自由,您可以为func. 如果该包的大小与提供的参数包的大小相同,则需要选择 的默认值opt。另一方面,如果它包含的参数多于函数所需的参数,您可以选择应使用哪个额外参数opt。在下面的示例中,我刚刚选择了第一个额外参数。
#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
}
Run Code Online (Sandbox Code Playgroud)