2016-10-05 2 views
1

Я пытаюсь использовать SFINAE для создания оболочек вызовов функций, используя std :: result_of, чтобы получить возвращаемый тип функции. Небольшой образец для воспроизведения проблемы:std :: result_of failing for void return type

void test(int) {} 

template<typename T, typename... Args, typename R = typename std::result_of<T(Args...)>::type, typename std::enable_if<!std::is_void<R>::value, int>::type* = nullptr> 
int test2(R& ret_param, Args... args) 
{ 
    T* test_call = test; 
    ret_param = test_call(args...); 
    return 0; 
} 

template<typename T, typename... Args, typename std::enable_if<std::is_void<typename std::result_of<T(Args...)>::type>::value, int>::type* = nullptr> 
int test2(Args... args) 
{ 
    T* test_call = test; 
    test_call(args...); 
    return 0; 
} 

int main() 
{ 
    test2<decltype(test)>(1); 
} 

компиляции это с GCC 4.9.2 результатов:

68:26: error: no matching function for call to 'test2(int)' 
68:26: note: candidates are: 
46:5: note: int test2(R&, Args ...) [with T = void(int); Args = {}; R = int; typename std::enable_if<(! std::is_void<R>::value), int>::type* <anonymous> = 0u] 
46:5: note: no known conversion for argument 1 from 'int' to 'int&' 
56:5: note: template<class T, class ... Args, typename std::enable_if<std::is_void<typename std::result_of<_Functor(_ArgTypes ...)>::type>::value, int>::type* <anonymous> > int test2(Args ...) 
56:5: note: template argument deduction/substitution failed: 
55:142: error: function returning a function 
55:142: note: invalid template non-type parameter 

тогда Проблема заключается в том, что какой-то образом «имяТип станд :: result_of :: типа» оценивает к функция возвращает функцию? Каков правильный способ использования SFINAE для принудительного разрешения перегрузки для выбора другой функции, если тип возврата функции шаблона недействителен? Стоит упомянуть, что в обратном случае, когда тестовая функция возвращает int. Кроме того, если enable_if удаляется для разрешения std :: is_void, он будет работать в этом случае (но очевидно, что тогда обе функции будут иметь правильное разрешение, если есть тип возврата, и компиляция завершится неудачно, когда он выбирает тот, который не ожидает возвращаемого значения).

+0

В качестве начала 'decltype (test)' является типом функции, и вы привязываете это к 'T', которое в логике включения рассматривается как возвращаемый тип. –

+0

Я бы использовал 'class' вместо' typename', и я бы избегал этих многосотни символов. Попытайтесь разделить вещи естественно, чтобы быть читаемыми. –

+0

Не следует, я понимаю, что std :: result_of :: type - это тип возвращаемого значения функции T, если вызывается с аргументами Args ... что я использую в логике включения. – Grahalt

ответ

1

По существу, вам необходимо сформировать T_ref(Args...) в качестве аргумента для std::result_of.

Следующие компиляции. Я применил несколько упрощений и переформатирования.

Кстати, я не думаю, что это хорошая идея. Вместо этого просто есть разные имена для двух функций. В конце концов, им нужны разные аргументы. Явный - это хорошо. Неявно плохо.

#include <type_traits>  // std::(enable_if, result_of) 

void test(int) {} 
int foo() { return 0; } 

template< 
    class T, class R, class... Args, 
    class T_ref = T&, 
    typename std::enable_if< 
     !std::is_void< 
      std::result_of_t<T_ref(Args...)> 
      >::value 
     >::type* = nullptr 
    > 
int test2(R& ret_param, Args... args) 
{ 
    static_assert(std::is_same< 
     R, 
     std::result_of_t<T_ref(Args...)> 
     >::value, "!"); 
    T* test_call = foo; 
    ret_param = test_call(args...); 
    return 0; 
} 

template< 
    class T, class... Args, 
    class T_ref = T&, 
    typename std::enable_if< 
     std::is_void< 
      std::result_of_t<T_ref(Args...)> 
      >::value 
     >::type* = nullptr 
    > 
int test2(Args... args) 
{ 
    T* test_call = test; 
    test_call(args...); 
    return 0; 
} 

int main() 
{ 
    test2<decltype(test)>(1); 

    int result; 
    test2<decltype(foo)>(result); 
} 
+0

В общем согласен, что неявный хороший vs явный, но абстрактно они делают то же самое, поэтому я хотел бы, чтобы они имели одно и то же имя, иначе это усложняло бы кучу кода в другом месте. Спасибо, например, есть ли что-то, что я могу взять T из ref изначально и избежать этого дополнительного параметра шаблона? Это отлично работает, просто пытаясь сделать его более красивым, чем на самом деле :) – Grahalt

+1

Вы можете просто написать 'T &' в 'result_of' и не добавлять дополнительный параметр шаблона. –

1

Если вы согласны передать функцию тестирования в качестве параметра, вам не нужно все, что шаблонный:

void test(int) {} 
int test(double) { return 0; } 

template<typename R, typename... Args> 
int test2(R(&f)(Args...), R& ret_param, Args... args) 
{ 
    ret_param = f(args...); 
    return 0; 
} 

template<typename... Args> 
int test2(void(&f)(Args...), Args... args) 
{ 
    f(args...); 
    return 0; 
} 

int main() 
{ 
    test2(test, 1); 
    int r; 
    test2(test, r, .0); 
} 

Вычета и перегрузка будут делать работу за вас.

+0

Спасибо за ответ, к сожалению, я не могу передать функцию в качестве параметра, поскольку у меня есть только объявление функции. Передача фактической функции просто даст мне ошибки компоновщика. Можно использовать только параметр decltype функции. – Grahalt

+0

@Grahalt Хорошо, вы положили определение в свой пример, поэтому я не мог этого знать. Прости. – skypjack

Смежные вопросы