如何将可变参数函数 fn<S, ...Args> 泛化为 fn<S0, ...Args0, S1, ...Args1, ... , SN, ...ArgsN>
How to generalize variadic function fn<S, ...Args> to fn<S0, ...Args0, S1, ...Args1, ... , SN, ...ArgsN>
假设有可变函数 template<typename... Args> foo(const S& s, Args... args)
。
struct S {};
template<typename... Args>
void foo(const S& s, Args... args);
void check_foo()
{
S s0{};
S s1{};
char a0 = 0;
short a1 = 1;
int a2 = 2;
foo(s0, a0); // OK
foo(s0, a0, a1); // OK
foo(s1, a1, a2); // OK
}
有没有办法编写扩展为 foo(s0, args0...); foo(s1, args1...); ...; foo(sN, argsN...);
的函数 bar(const S& s0, Args0...args0, const S& s1, Args1...args1, ..., const S& sN, ArgsN...argsN)
?
我试过了(没有成功):
#include <type_traits>
template<typename... Args,
typename = std::enable_if_t<
!(... || std::is_same_v<S, Args>)
>
>
void bar(const S& s, Args... args)
{
foo(s, args...);
}
template<typename... Args0, typename... Args1>
void bar(
const S& s0, Args0... args0,
const S& s1, Args1... args1)
{
bar(s0, args0...); // ??? call to foo
bar(s1, args1...); // ??? recursive to bar or to foo
}
void check_bar()
{
S s0{};
S s1{};
char a0 = 0;
short a1 = 1;
int a2 = 2;
bar(s0, a0); // OK
bar(s1, a1, a2); // OK
bar(s0, a0,
s0, a0, a1,
s1, a1, a2); // fail
}
如果我理解正确,编译器 (clang 9.0.0) 总是将 Args0 扩展为空列表并且匹配失败。
大致如下:
template <typename... Args>
void bar(const S& s, Args... args);
template<typename Tuple, std::size_t... Is>
void call_foo(const Tuple& t, std::index_sequence<Is...>) {
foo(std::get<Is>(t)...);
}
template<size_t nextS, typename Tuple, std::size_t... Is>
void call_bar(const Tuple& t, std::index_sequence<Is...>) {
bar(std::get<Is + nextS>(t)...);
}
template <typename... Args, std::size_t... Is>
void bar_helper(const S& s, std::index_sequence<Is...>, Args... args) {
constexpr size_t N = sizeof...(Is);
if constexpr (N == 0) {
foo(s);
}
if constexpr (N > 0) {
constexpr size_t nextS = 1 + std::min<std::size_t>(
{(std::is_same_v<S, std::decay_t<Args>> ? Is : N) ...});
auto t = std::forward_as_tuple(s, args...);
call_foo(t, std::make_index_sequence<nextS>{});
if constexpr (nextS <= N) {
call_bar<nextS>(t, std::make_index_sequence<N - nextS + 1>{});
}
}
}
template <typename... Args>
void bar(const S& s, Args... args) {
bar_helper(s, std::make_index_sequence<sizeof...(Args)>{}, args...);
}
假设有可变函数 template<typename... Args> foo(const S& s, Args... args)
。
struct S {};
template<typename... Args>
void foo(const S& s, Args... args);
void check_foo()
{
S s0{};
S s1{};
char a0 = 0;
short a1 = 1;
int a2 = 2;
foo(s0, a0); // OK
foo(s0, a0, a1); // OK
foo(s1, a1, a2); // OK
}
有没有办法编写扩展为 foo(s0, args0...); foo(s1, args1...); ...; foo(sN, argsN...);
的函数 bar(const S& s0, Args0...args0, const S& s1, Args1...args1, ..., const S& sN, ArgsN...argsN)
?
我试过了(没有成功):
#include <type_traits>
template<typename... Args,
typename = std::enable_if_t<
!(... || std::is_same_v<S, Args>)
>
>
void bar(const S& s, Args... args)
{
foo(s, args...);
}
template<typename... Args0, typename... Args1>
void bar(
const S& s0, Args0... args0,
const S& s1, Args1... args1)
{
bar(s0, args0...); // ??? call to foo
bar(s1, args1...); // ??? recursive to bar or to foo
}
void check_bar()
{
S s0{};
S s1{};
char a0 = 0;
short a1 = 1;
int a2 = 2;
bar(s0, a0); // OK
bar(s1, a1, a2); // OK
bar(s0, a0,
s0, a0, a1,
s1, a1, a2); // fail
}
如果我理解正确,编译器 (clang 9.0.0) 总是将 Args0 扩展为空列表并且匹配失败。
大致如下:
template <typename... Args>
void bar(const S& s, Args... args);
template<typename Tuple, std::size_t... Is>
void call_foo(const Tuple& t, std::index_sequence<Is...>) {
foo(std::get<Is>(t)...);
}
template<size_t nextS, typename Tuple, std::size_t... Is>
void call_bar(const Tuple& t, std::index_sequence<Is...>) {
bar(std::get<Is + nextS>(t)...);
}
template <typename... Args, std::size_t... Is>
void bar_helper(const S& s, std::index_sequence<Is...>, Args... args) {
constexpr size_t N = sizeof...(Is);
if constexpr (N == 0) {
foo(s);
}
if constexpr (N > 0) {
constexpr size_t nextS = 1 + std::min<std::size_t>(
{(std::is_same_v<S, std::decay_t<Args>> ? Is : N) ...});
auto t = std::forward_as_tuple(s, args...);
call_foo(t, std::make_index_sequence<nextS>{});
if constexpr (nextS <= N) {
call_bar<nextS>(t, std::make_index_sequence<N - nextS + 1>{});
}
}
}
template <typename... Args>
void bar(const S& s, Args... args) {
bar_helper(s, std::make_index_sequence<sizeof...(Args)>{}, args...);
}