在C++17中,实现一个overload(fs...)
函数变得简单,该函数接受任意数量的参数fs...
,这些参数满足FunctionObject
概念,并返回一个新的函数对象,其行为类似于fs...
的重载。例如:
template <typename... Ts>
struct overloader : Ts...
{
template <typename... TArgs>
overloader(TArgs&&... xs) : Ts{forward<TArgs>(xs)}...
{
}
using Ts::operator()...;
};
template <typename... Ts>
auto overload(Ts&&... xs)
{
return overloader<decay_t<Ts>...>{forward<Ts>(xs)...};
}
int main()
{
auto o = overload([](char){ cout << "CHAR"; },
[](int) { cout << "INT"; });
o('a'); // prints "CHAR"
o(0); // prints "INT"
}
由于上述overloader
继承自Ts...
,因此它需要复制或移动函数对象才能正常工作。 我希望有一种提供相同重载行为但仅引用传递的函数对象的方法。
假设我们称该假想函数为ref_overload(fs...)
。我的尝试是使用std::reference_wrapper
和std::ref
,如下所示:
template <typename... Ts>
auto ref_overload(Ts&... xs)
{
return overloader<reference_wrapper<Ts>...>{ref(xs)...};
}
看起来很简单,对吧?
int main()
{
auto l0 = [](char){ cout << "CHAR"; };
auto l1 = [](int) { cout << "INT"; };
auto o = ref_overload(l0, l1);
o('a'); // BOOM
o(0);
}
error: call of '(overloader<...>) (char)' is ambiguous
o('a'); // BOOM
^
它不能工作的原因很简单:std::reference_wrapper::operator()
是一个 变参函数模板,与重载不兼容。
为了使用 using Ts::operator()...
语法,我需要使 Ts...
满足 FunctionObject
。如果我试图制作自己的 FunctionObject
包装器,我会遇到同样的问题:
template <typename TF>
struct function_ref
{
TF& _f;
decltype(auto) operator()(/* ??? */);
};
由于无法表达"编译器,请使用与 TF :: operator()
相同的参数填充 ???
",因此我需要使用可变函数模板,却没有解决任何问题。
我也不能使用类似于boost :: function_traits
的东西,因为传递给overload(...)
的函数之一可能是函数模板或重载函数对象本身!
因此我的问题是:是否有一种实现ref_overload(fs ...)
函数的方法,它给定任意数量的fs ...
函数对象,返回一个新的函数对象,其行为像 fs ...
的重载,但引用fs ...
而不是复制/移动它们?
using
声明将所有那些operator()
带入你自己的重载集合中。从而允许C ++编译器为你执行重载决议的工作。一旦你不能再使用这个技巧,你现在就必须手动实现重载决议。那将是…相当棘手的。祝你好运。 - Nicol Bolas