我能否根据其运算符()的签名专门化可变参数模板参数
Can I specialize a variadic template argument based on the signature of its operator()
假设我有一个这样的函数
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()
的东西。
我可以直接写一个合适的专业,还是我必须用特征类和SFINAE做一些奇怪的事情?
你可以做这样的事情:
#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>;
演示
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++14overloaded
如下所示:
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)... )
{}
};
我认为这对我们的目的来说已经足够了。 (更一般地说,你要么做一个二叉树,平衡与否(,并处理完美的转发,然后......等。
相关文章:
- 是否可以对零模板参数进行模板专门化
- 我能否根据其运算符()的签名专门化可变参数模板参数
- 取消专门化C++模板参数
- 模板函数,其中一个参数需要专门化,而另一个不需要
- 如何为特定数量的模板参数专门化可变参数模板结构
- 使用非类型模板参数进行专门化模板模板参数
- g++和clang++在结构/类专门化中具有非类型参数的不同行为
- 我如何避免使用依赖于参数的查找明确专门化模板化功能
- 专门化采用通用引用参数的函数模板
- 如果需要参数包,则使用SFINAE专门化类
- c++多参数模板化的类成员专门化
- C++11`using`关键字:专门化模板参数的模板别名
- 如何在没有参数的情况下专门化模板类
- 我可以用非模板类专门化一个可变模板模板模板参数吗
- 如何在C++中专门化 std::vector 的模板模板参数
- 使用已推导的模板参数专门化模板成员函数
- 如何根据类的模板参数专门化模板成员函数?
- 基于嵌套的内部参数专门化模板
- 如何用模板模板参数专门化模板类的成员
- 使用模板类作为参数专门化模板结构