2017-01-24 13 views
7
#include <vector> 
#include <functional> 

template<class F> 
class Foo 
{ 
public: 
    template <class T> 
    void std_function(std::function<F(std::vector<T>)> functor) 
    { 
     /* something */ 
    } 

    template <class T> 
    void func_ptr(F (*funtor)(std::vector<T>)) 
    { 
     /* something else */ 
    } 
}; 

template<class T, class F> 
F bar(std::vector<T>) 
{ 
    return F(); 
} 

int main() 
{ 
    Foo<double> test; 
    std::function<double(std::vector<int>)> barz = bar<int, double>; 

    test.std_function(bar<int, double>); //error 1 
    test.std_function(barz); //OK 1 
    test.func_ptr(bar<int, double>); //OK 2 

    test.std_function(bar<int>); //error 2::1 
    test.func_ptr(bar<int>); //error 2::2 

    return 0; 
} 

Вопрос 1.Передача (частично) шаблонный шаблон функции, как станд :: функции (или указатель на функцию)

линия ошибка 1: Я пытаюсь передать явно инстанцированный шаблон функции (bar<int, double>) как std::function, но это не законно.

линия OK 1: Если я обернуть bar<int, double> в std::function<double(std::vector<int>)> и передать обернутый функтор, это законно в настоящее время.

линия OK 2: Если я прохожу через bar<int, double>Foo::func_ptr, который получает указатель на функцию в качестве аргумента вместо std::function, он также законно.

Я хочу сделать линию error 1 legal. Как и в Линии OK 2, можно пройти bar<int, double> без какой-либо обертки (в отличие от линии OK 1) и хранить ту же форму. Но тип параметра отличается. Я хочу передать как std::function, а не указатель на функцию.

Вопрос 2.

Line Ошибка 2 :: 1 и 2 :: 2: То, что я пытаюсь достичь здесь есть, я хочу класс Foo вывести тип возвращаемого bar в качестве шаблона класса тип F (для кода выше, F is double). Поэтому я могу просто пройти как bar<int>, а не bar<int, double>.

Но, похоже, у вас нет вычетов, потому что даже если я прохожу bar<int> через Foo::func_ptr, он по-прежнему вызывает ошибку. Как я могу заставить этот код работать как мое намерение?

ответ

3

Для ошибки 1, что происходит в том, что компилятор пытается заменить T в std::function, но она не может, потому что в конечном счете указатель функции и std::function различные типы, и нет никакого преобразования определяется для указателя функции для std::function

Это сработало:

std::function<double(std::vector<int>)> barz = bar<int, double> 

Поскольку std::function был искусно написано с типом-стиранием иметь конструктор, который может принимать любые отзывной, который конвертируется в тип он нуждается. Обратите внимание, что это не то же самое, что тип-вывод в приведенной выше ошибке, потому что здесь мы уже указываем аргументы шаблона для std::function.

Обратите внимание, что мы можем сделать небольшую работу, чтобы получить Foo::std_function для правильной работы. Во-первых изменить свою подпись взять ссылку переадресации:

template <class T> 
void std_function(T&& functor){/*I'll talk about this in a bit*\} 

Тогда мы можем строить std::function внутренне (То, что вы хотите передать в него, я не знаю) с помощью некоторых вспомогательных структур, чтобы определить его тип ,Для указателя функции мы можем сделать следующее:

// base 
template<class... T> 
struct function_type_impl; 

// specialization for function ptrs and static class fns 
template<class Ret, class... Args> 
struct function_type_impl<Ret(*)(Args...)> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

// type alias so we don't need to keep typing typename ... ::type 
template<class... T> 
using function_type = typename function_type_impl<std::decay_t<T>...>::type; 

, а затем мы можем изменить нашу std_function подпись:

template <class T> 
void std_function(T&& functor) 
{ 
    function_type<T> myFunction = std::forward<T>(functor); 
    // do something with our std::function 
} 

Тогда вы можете назвать его как

test.std_function(&::bar<int, double>); 

Однако, если мы хотят быть более полными и принимать функторы, лямбды и даже другие std::functions, мы можем добавить дополнительные специализации:

namespace detail 
{ 
template<class... T> 
struct function_type_impl; 

template<class Callable> 
struct function_type_impl<Callable> 
{ 
    using type = typename function_type_impl<decltype(&Callable::operator())>::type; 
}; 

template<class C, class Ret, class... Args> 
struct function_type_impl<Ret(C::*)(Args...) const> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

template<class Ret, class... Args> 
struct function_type_impl<Ret(*)(Args...)> 
{ 
    using type = std::function<Ret(Args...)>; 
}; 

template<class... T> 
using function_type = typename function_type_impl<std::decay_t<T>...>::type; 
}// detail namespace 

А теперь следующий будет работать, тоже:

struct MyFunctor 
{ 
    double operator()(std::vector<int>) const 
    { 
     return 42; 
    } 
}; 

struct MyFunctor2 
{ 
    static double foo(std::vector<int>) 
    { 
     return 42; 
    } 
}; 

int main() 
{ 
    Foo<double> test; 
    std::function<double(std::vector<int>)> barz = bar<int, double>; 
    test.std_function(&::bar<int, double>); 
    test.std_function(barz); 
    test.std_function([](std::vector<int>)->double{return 42;}); 
    test.std_function(MyFunctor{}); 
    test.std_function(MyFunctor2::foo); 
} 

Live Demo

Для ошибок 2 :: 1 и 2 :: 2, вопрос проще; функции вообще не существуют до тех пор, пока они не будут полностью созданы. То есть вы не можете создать указатель на функцию частично шаблонов. Вы должны указать все аргументы шаблона при попытке получить указатель на функцию. Так как вы уже указали тип возвращаемого значения, вы можете позволить компилятору инстанцировать остальную часть указателя для вас, если вы явно указать func_ptr, что вывести на T:

test.func_ptr<int>(bar); 
+2

Для пункта 2: [ничтожной counter_example (двойная (*) (std :: vector ));] (http://coliru.stacked-crooked.com/a/eb9979b58f3f76e7) - Я могу преобразовать шаблон функции в указатель функции без передачи каких-либо аргументов шаблона. – Yakk

+0

@Yakk: Да, я этого не знал. Спасибо за обучение. Сообщение обновлено. – AndyG

+0

Итак, использование указателя функции - лучший выбор в этом случае? – Gear