ada*_*dam 16 c++ templates template-meta-programming c++11
我想创建一个编译类型的函数,给定任何可调用的对象f(函数,lambda表达式,函数对象,...)和类型T,如果f可以使用类型的参数调用,则求值为true,如果是,则为Tfalse这不可以.
例:
void f1(int) { ... }
void f2(const std::string&) { ... }
assert( is_callable_with<int>(f1));
assert(!is_callable_with<int>(f2));
Run Code Online (Sandbox Code Playgroud)
我认为巧妙地使用SFINAE规则可以实现这一目标.可能有点像这样:
template<typename T, typename F>
constexpr bool is_callable_with(F&&, typename std::result_of<F(T)>::type* = nullptr) {
return true;
}
template<typename T, typename F>
constexpr bool is_callable_with(F&&) {
return false;
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为如果F可调用T,则两个重载都参与重载决策并且存在歧义.我想重写它,所以在正面情况下,第一个重载将由第二个重载决策选择.不确定我是否在这里的正确轨道.
iav*_*avr 11
保罗答案的一个变体,但遵循标准的SFINAE测试模式.同样具有任意参数类型的通用特征A...:
struct can_call_test
{
template<typename F, typename... A>
static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
f(int);
template<typename F, typename... A>
static std::false_type
f(...);
};
template<typename F, typename... A>
using can_call = decltype(can_call_test::f<F, A...>(0));
Run Code Online (Sandbox Code Playgroud)
然后constexpr你要求的功能:
template<typename T, typename F>
constexpr bool is_callable_with(F&&) { return can_call<F, T>{}; }
Run Code Online (Sandbox Code Playgroud)
查看实时示例.
这将与函数,lambda表达式或具有任意数量参数的函数对象一起使用,但对于(指向)您必须使用的成员函数std::result_of<F(A...)>.
UPDATE
下面,can_call有一个很好的"函数签名"语法std::result_of:
template<typename F, typename... A>
struct can_call : decltype(can_call_test::f<F, A...>(0)) { };
template<typename F, typename... A>
struct can_call <F(A...)> : can_call <F, A...> { };
Run Code Online (Sandbox Code Playgroud)
像这样使用
template<typename... A, typename F>
constexpr can_call<F, A...>
is_callable_with(F&&) { return can_call<F(A...)>{}; }
Run Code Online (Sandbox Code Playgroud)
我也做了is_callable_withvariadic(我不明白为什么它应该限于一个参数)并返回相同的类型can_call而不是bool(感谢Yakk).
再次,这里的实例.
我会先做一个类型特征:
template<class X = void>
struct holder
{
typedef void type;
};
template<class F, class T, class X = void>
struct is_callable_with_trait
: std::false_type
{};
template<class F, class T>
struct is_callable_with_trait<F, T, typename holder<
decltype(std::declval<F>()(std::declval<T>()))
>::type>
: std::true_type
{};
Run Code Online (Sandbox Code Playgroud)
然后,如果你想,你可以把它变成一个函数:
template<typename T, typename F>
constexpr bool is_callable_with(F&&)
{
return is_callable_with_trait<F&&, T>::value;
}
Run Code Online (Sandbox Code Playgroud)
template<class F, class T, class = void>
struct is_callable_with_impl : std::false_type {};
template<class F, class T>
struct is_callable_with_impl<F,T,
typename std::conditional<
true,
void,
decltype( std::declval<F>() (std::declval<T>()) ) >::type
> : std::true_type {};
template<class T, class F>
constexpr bool is_callable_with(F &&)
{
return is_callable_with_impl< F, T >::value;
}
Run Code Online (Sandbox Code Playgroud)
它与发布的解决方案基本相同Paul,我更喜欢使用conditional<true, void, decltype( ... ) >而不是holder类来避免命名空间污染.