如何在编译时检查是不是存在可以使用特定参数集调用的函数?
Posted
技术标签:
【中文标题】如何在编译时检查是不是存在可以使用特定参数集调用的函数?【英文标题】:How to check at compile-time if a function that can be called with a specific set of arguments exists?如何在编译时检查是否存在可以使用特定参数集调用的函数? 【发布时间】:2015-02-19 20:32:27 【问题描述】:这与检查是否定义了特定函数不同。在这里,为了让这个检查返回true
,必须定义函数并且传递某种类型的参数应该会导致有效的调用。
示例:对于函数f
和T &&
类型的参数,如果f
是直接或通过隐式转换接受@ 类型参数的有效函数,则检查应返回true
987654327@.
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;
请注意this answer 中解释的“参数”和“参数”之间的区别。
【问题讨论】:
【参考方案1】:利用 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);
借助更多代码(可在this Gist 中获得),可以输出表格,显示可以将什么类型的参数传递给什么类型的参数。
using T = Default (empty struct with implicit constructors):
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
| | |
| Function signature | Argument type |
| | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| | T | const T | volatile T | const volatile T | T & | const T & | volatile T & | const volatile T & | T && |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &) | x | | | | x | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T &) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T &) | x | | x | | x | | x | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T &) | x | x | x | x | x | x | x | x | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &&) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
using T = NonCopiable:
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
| | |
| Function signature | Argument type |
| | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| | T | const T | volatile T | const volatile T | T & | const T & | volatile T & | const volatile T & | T && |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &) | x | | | | x | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T &) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T &) | x | | x | | x | | x | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T &) | x | x | x | x | x | x | x | x | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &&) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
using T = NonMovable:
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
| | |
| Function signature | Argument type |
| | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| | T | const T | volatile T | const volatile T | T & | const T & | volatile T & | const volatile T & | T && |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T) | x | x | | | x | x | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T) | x | x | | | x | x | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T) | x | x | | | x | x | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T) | x | x | | | x | x | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &) | x | | | | x | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T &) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T &) | x | | x | | x | | x | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T &) | x | x | x | x | x | x | x | x | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &&) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
using T = NonCopiableNonMovable:
+--------------------------------+---------------------------------------------------------------------------------------------------------------------------------+
| | |
| Function signature | Argument type |
| | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| | T | const T | volatile T | const volatile T | T & | const T & | volatile T & | const volatile T & | T && |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T) | | | | | | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T) | | | | | | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T) | | | | | | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T) | | | | | | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &) | x | | | | x | | | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const T &) | x | x | | | x | x | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(volatile T &) | x | | x | | x | | x | | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(const volatile T &) | x | x | x | x | x | x | x | x | |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
| function(T &&) | | | | | | | | | x |
+--------------------------------+-----+-----------+---------------+---------------------+-------+-------------+-----------------+-----------------------+---------+
例如,我们可以从这些表中推断出T
类型的参数不能传递给以T &&
作为参数的函数。或者function(T &&)
只接受T &&
类型的参数。
请注意删除副本和/或移动构造函数如何减少可能性,因为参数不能再隐式转换。
编辑:
感谢@hvd,增加了对成员函数的支持。
【讨论】:
只是一个警告:如果你的函数超载,这将无法工作。 没错,感谢您指出这一点!这也是为什么在 Gist 代码中我必须将函数包装在一个结构中的原因。最重要的是,它也不适用于成员函数。不过,我想不出解决这两个缺陷的方法,知道吗? 您可以通过将成员函数转换为常规函数来处理它们:使用从Ret (T::*) (Args...)
中提取Ret (Args...)
的类型特征,然后您可以使用已有的。但我不认为有任何方法可以让重载函数工作。
我想我有一个类型特征可以像你建议的那样工作 (pastebin.com/2vCdKs4u),但我不知道如何将它集成到我的代码中——对我来说一次太多新概念了!跨度>
isFunctionCallable<int>(f)
将T_Function
推导出为void(*)(int&)
,而不是void(int&)
,并且您对ExtractFunction
的实现还没有处理指向函数的指针。为此添加一个专业化,我得到1
作为输出。【参考方案2】:
#define overload_set(F)\
struct auto operator()(auto&&...args)const\
->decltype(F(std::forward<decltype(args)>(args)...))\
return (F(std::forward<decltype(args)>(args)...)); \
这需要一个标记F
并为F
生成一个重载集类型。
它不是很完美:它只能通过 SFINAE 测试进行完美转发。但它很接近。
然后我们使用这个:
template<class T,class=void>struct can_invoke:std::false_type;
template<class F,class...Args>
struct can_invoke<F(Args...),
decltype(void(
std::declval<F>()(std::declval<Args>()...)
))
>:std::true_type;
混合它们我们得到:
typedef overload_set(Foo) Foo_overloads;
std::cout << can_invoke<Foo_overloads(int, int) >::value<<"\n";
如果Foo(3,2)
有效,将打印1
。如前所述,这受到完美转发失败的限制。
您也可以将Foo_overloads
传递给需要函数对象的函数,它会在调用站点调度而不是在传递函数对象时执行。
【讨论】:
以上是关于如何在编译时检查是不是存在可以使用特定参数集调用的函数?的主要内容,如果未能解决你的问题,请参考以下文章