假设我有这样的功能
template <typename... FunctionList>
void call_all (int i, float f, const FunctionList... function_list);
template <>
void call_all (int, float)
{
}
我想专门化这样的东西:
template <typename HasIntArgument, typename... FL>
void call_all (int i, float f, const HasIntArgument & hia, const FL... list)
{
hia (i);
call_all (i, f, list...);
}
template <typename HasFloatArgument, typename... FL>
void call_all (int i, float f, const HasFloatArgument & hfa, const FL... list)
{
hfa (f);
call_all (i, f, list...);
}
换句话说,我希望此函数对于function_list
中的每个函数类对象,确定它是否可以通过签名void(int)
或void(float)
进行调用。 (此列表中的任何内容都不能被多个签名调用。)
我希望它可以与原始函数指针,lambda或具有适当operator()
的任何东西一起使用。
我可以直接编写合适的专业化知识吗,还是必须对traits类和SFINAE做一些奇怪的事情?
答案 0 :(得分:1)
您可能会做类似的事情:
#if 0 // C++17
template <typename F>
void dispatch(F func, int i, float f)
{
if constexpr (has_int_argument<F>::value) {
func(i);
} else {
func(f);
}
}
#else // C++11
template <typename F>
typename std::enable_if<has_int_argument<F>::value>::type
dispatch(F func, int i, float)
{
func(i);
}
template <typename F>
typename std::enable_if<!has_int_argument<F>::value>::type
dispatch(F func, int, float f)
{
func(f);
}
#endif
template <typename... Fs>
void call_all (int i, float f, const Fs&... fs)
{
// (dispatch(fs, i, f), ...); // C++17
const int dummy[] = {0, (dispatch(fs, i, f), 0)...};
static_cast<void>(dummy);
}
具有适当的功能特征has_int_argument
。像这样:
template <typename ClassOrSig> struct funct_trait;
template <typename C>
struct funct_trait : funct_trait<decltype(&C::operator())> {};
template <typename C, typename Ret, typename ...Args>
struct funct_trait<Ret (C::*) (Args...)> : funct_trait<Ret(Args...)> {};
template <typename C, typename Ret, typename ...Args>
struct funct_trait<Ret (C::*) (Args...) const> : funct_trait<Ret(Args...)> {};
// &&, &, volatile, ... (C ellipsis)
template <typename Ret, typename ...Args>
struct funct_trait<Ret (*)(Args...)> : funct_trait<Ret(Args...)> {};
template <typename Ret, typename ...Args>
struct funct_trait<Ret (Args...)>
{
using sig_type = Ret(Args...);
using args_tuple = std::tuple<Args...>;
// ...
};
template <typename T>
using has_int_argument = std::is_same<std::tuple<int>,
typename funct_trait<T>::args_tuple>;
答案 1 :(得分:0)
template<class...Fs>struct overloaded:Fs...{
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs...)->overloaded<Fs...>;
在c++14中,上述操作有些棘手,但是实现到处都是。
namespace details {
struct secret_tag {};
struct secret_result {
template<class...Ts>
secret_tag operator()(Ts&&...) const;
};
template<class F>
using secret_tester = overloaded<std::decay_t<F>, secret_result>;
}
template<class F, class Arg>
using match_arg_exactly = std::integral_constant<
bool,
!std::is_same<
details::secret_tag,
std::result_of_t< details::secret_tester<F>(Arg) >
>{}
>;
现在我们可以要求给定的对象是否可以精确匹配特定的参数。
template <typename HasIntArgument>
void call_one(int i, float f, std::true_type, const HasIntArgument & hia)
{
hia (i);
}
template <typename HasFloatArgument>
void call_one(int i, float f, std::false_type, const HasFloatArgument& hia)
{
hia (f);
}
template <typename F>
void call_one(int i, float f, const F & hia)
{
call_one( i, f, match_arg_exactly<const F&, int>{}, hia );
}
我们使用这个:
void call_all (int, float)
{}
template<class F, class...Fs>
void call_all (int i, float f, F const& f0, Fs const&...fs) {
call_one( i, f, f0 );
call_all(i, f, fs...);
}
测试代码:
struct float_eater {
void operator()(float x)const{ std::cout<< "float "<<x<<"\n"; }
};
struct int_eater {
void operator()(int x)const{ std::cout<< "int "<<x<<"\n"; }
};
call_all( 42, 3.14, float_eater{}, int_eater{}, int_eater{} );
c++14 overloaded
类似于:
template<class...Fs>
struct overloaded;
template<class F0>
struct overloaded<F0>:F0 {
overloaded(F0 f0):F0(std::move(f0)) {}
using F0::operator();
};
template<class F0, class F1>
struct overloaded<F0, F1>: F0, F1 {
overloaded( F0 f0, F1 f1 ):F0(std::move(f0)), F1(std::move(f1)) {}
using F0::operator();
using F1::operator();
};
template<class F0, class...Fs>
struct overloaded<F0, Fs...>:
overloaded<F0, overloaded<Fs...>>
{
overloaded(F0 f0, Fs...fs):
F0(std::move(f0)),
overloaded<Fs...>( std::move(fs)... )
{}
};
我认为这足以满足我们的目的。 (更一般而言,您可以制作一个二叉树(是否平衡),并处理完美的转发,等等。