C++ 如何在编译时检查是否存在可以使用一组特定参数调用的函数?

C++ 如何在编译时检查是否存在可以使用一组特定参数调用的函数?,c++,function,templates,compile-time,C++,Function,Templates,Compile Time,这与检查是否定义了特定函数不同。在这里,要使此检查返回true,必须定义函数,并且传递特定类型的参数应导致有效调用 示例:对于函数f和类型为T&的参数,如果f是直接或通过隐式转换接受类型为T&的参数的有效函数,则检查应返回true void f(int &) {}; int main(int argc, char **av) { isFunctionCallable<int>(f); // true because `int i; f(i);` is valid.

这与检查是否定义了特定函数不同。在这里,要使此检查返回
true
,必须定义函数,并且传递特定类型的参数应导致有效调用

示例:对于函数
f
和类型为
T&
的参数,如果
f
是直接或通过隐式转换接受类型为
T&
的参数的有效函数,则检查应返回
true

void f(int &) {};

int main(int argc, char **av)
{
    isFunctionCallable<int>(f); // true because `int i; f(i);` is valid.
    isFunctionCallable<int &&>(f); // false because `int i; f(std::move(i));` is invalid.
    return 0;
}
void f(int&){};
内部主(内部argc,字符**av)
{
isFunctionCallable(f);//为true,因为'int i;f(i);'有效。
isFunctionCallable(f);//false,因为`int i;f(std::move(i));`无效。
返回0;
}

请注意中解释的“参数”和“参数”之间的区别。

使用C++11,这可以使用SFINAE、
decltype
std::declval
的混合来完成

template<typename ...>
struct Bool
{ using type = bool; };

template<typename ... T_Dummies>
using BoolT = typename Bool<T_Dummies ...>::type;


template<typename T>
struct DeclvalType
{
    using type = typename std::conditional<
        std::is_rvalue_reference<T>::value,
        T,
        T &
    >::type;
};

template<typename T>
using DeclvalTypeT = typename DeclvalType<T>::type;


template<typename T>
struct ExtractFunction;

template<typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(T_Args ...)>
{ using type = T_Return(T_Args ...); };

template<typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(*)(T_Args ...)>
{ using type = T_Return(T_Args ...); };

template<typename T, typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(T::*)(T_Args ...)>
{ using type = T_Return(T_Args ...); };

template<typename T, typename T_Return, typename ... T_Args>
struct ExtractFunction<T_Return(T::*)(T_Args ...) const>
{ using type = T_Return(T_Args ...); };

template<typename T>
using ExtractFunctionT = typename ExtractFunction<T>::type;


template<typename ... T, typename T_Function>
constexpr auto
impl(T_Function function) ->
    BoolT<decltype(
        std::declval<ExtractFunctionT<T_Function>>()
            (std::declval<DeclvalTypeT<T>>() ...)
    )>
{ return true; }

template<typename ... T>
constexpr bool
impl(...)
{ return false; }


template<typename ... T, typename T_Function>
constexpr bool
isFunctionCallable(T_Function function)
{ return impl<T ...>(function); }
例如,我们可以从这些表中推断,
T
类型的参数不能传递给以
T&
为参数的函数。或者
函数(T&&
只接受
T&
类型的参数

void f(int &) {};

int main(int argc, char **av)
{
    isFunctionCallable<int>(f); // true because `int i; f(i);` is valid.
    isFunctionCallable<int &&>(f); // false because `int i; f(std::move(i));` is invalid.
    return 0;
}
请注意,删除副本和/或移动构造函数如何减少可能发生的情况,因为参数不能再进行隐式转换

编辑:

由于@hvd,增加了对成员函数的支持

#define overload_set(F)\
  struct { auto operator()(auto&&...args)const\
    ->decltype(F(std::forward<decltype(args)>(args)...))\
     { return (F(std::forward<decltype(args)>(args)...)); }\
  }
将它们混合在一起,我们得到:

typedef overload_set(Foo) Foo_overloads;
std::cout << can_invoke<Foo_overloads(int, int) >::value<<"\n";
typedef重载集(Foo)Foo重载;

std::这仅仅是一个警告:如果您的函数过载,这将无法工作。是的,谢谢您指出这一点!这也是为什么在Gist代码中我必须将函数包装在结构中的原因。除此之外,它对成员函数也不起作用。但是,我想不出一种方法来修复这两个缺陷,你可以通过将成员函数转换成常规函数来处理它们:使用一个类型特征,从
Ret(t::*)(Args…
)中提取
Ret(Args…
,然后你可以使用你已经拥有的。但是我不认为有任何方法可以让重载函数工作。我认为我有一个类型特征,可以像你建议的那样工作(),但我不知道如何在代码中集成它,一次给我带来太多新概念
isFunctionCallable(f)
T_函数
推断为
void(*)(int&)
,而不是
void(int&)
,并且
ExtractFunction
的实现还没有处理函数指针。为此添加一个专门化,我得到
1
作为输出。
typedef overload_set(Foo) Foo_overloads;
std::cout << can_invoke<Foo_overloads(int, int) >::value<<"\n";