2014-12-21 6 views
3

Это отличается от проверки, определена ли определенная функция. Здесь, чтобы эта проверка вернула true, функция должна быть определена, и передача аргументов определенного типа должна привести к действительному вызову.Как проверить во время компиляции, если существует функция, которая может быть вызвана с определенным набором аргументов?

Пример: для функции f и аргумент типа T &&, то проверка должна возвращать true, если f является действительной функцией, которая принимает-либо непосредственно, либо через неявное преобразование аргумента-типа 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; 
} 

Пожалуйста, обратите внимание на различие между «аргументами» и «параметры», как описано в this answer.

ответ

6

Используя 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 &&.

Обратите внимание, что удаление копии и/или конструктора перемещения уменьшает возможности, поскольку аргументы не могут быть преобразованы неявно.

Edit:

Добавлена ​​поддержка для функций-членов, благодаря @hvd.

+0

Просто предупреждение: это невозможно сделать, если ваша функция перегружена. – hvd

+0

Правда, спасибо, что указали это! Вот почему в коде Gist мне пришлось обернуть функции внутри структуры. Кроме того, он не будет работать и с функциями-членами. Я не могу придумать, как исправить эти два недостатка, хотя идея? – ChristopherC

+1

Вы можете иметь дело с функциями-членами, превратив их в обычные функции: использовать черту типа, которая извлекает 'Ret (Args ...)' из 'Ret (T :: *) (Args ...)', а затем вы можете используйте то, что у вас уже есть. Но я не думаю, что есть способ получить что-то, что работает для перегруженных функций. – hvd

3
#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"; 

напечатает 1 если таковые Foo(3,2) работы. Как уже отмечалось, это ограничивается отказами от совершенной пересылки.

Вы также можете передать Foo_overloads{} функциям, ожидающим функциональный объект, и он отправит на сайт вызова вместо того, чтобы делать это при передаче объекта функции.