2013-06-19 127 views
2

我正在尋找一種方式來函數指針,仿函數或lambda表達式傳遞給模板函數g它使用傳遞函數的參數類型,例如:函數和仿函數作爲參數傳遞給模板函數

template<class T1, class T2, class T3> 
struct wrapper_t { 
    boost::function<void(T1,T2,T3)> f; 
    wrapper_t(boost::function<void(T1,T2,T3)> f) : f(f) {} 
    void operator(std::vector<T1> &a, std::vector<T2> &b, T3 c) { 
    assert(a.size() == b.size()); 
    for(size_t i = 0 ; i != a.size() ; i++) f(a[i], b[i], c); 
    } 
}; 
template<class T1, class T2, class T3> 
wrapper_t<T1,T2,T3> make_wrapper(boost::function<void(T1,T2,T3)> f) { 
    return wrapper_t<T1,T2,T3>(f); 
} 

void f(int, double, char) {}; 
wrapper_t<int, double, char> w0(f); // need to repeat types 

auto w1 = make_wrapper(f); // more comfortable 

std::vector<int> a{{1, 2, 3}}; 
std::vector<double> b{{1.0, 2.0, 3.0}}; 
w0(a, b, 'c'); 
w1(a, b, 'c'); 

make_wrapper函數只存在從參數中提取類型,一些語法糖以避免必須輸入兩次。


我的問題一個最小的例子是這樣的功能:

template<class T> 
void g1(const boost::function<void(T)> &) {} 

使用這些輸入

void f1(int) {} 
struct f2_t { void operator()(int) {} }; 

它無法推斷T=int

f2_t f2; 
g1(f1); // mismatched types ‘const std::function<void(T)>’ and ‘void(int)’ 
g1(f2); // ‘f2_t’ is not derived from ‘const std::function<void(T)>’ 
g1([](int){}); // ‘::<lambda(int)>’ is not derived from ‘… 
g1<int>(f1); // ok 
g1<int>(f2); // ok 
g1<int>([](int){}); // ok 

T=int可以從一個普通的函數指針推斷,但是這並不仿函數或lambda工作之一:

template<class T> 
void g2(void (*)(T)) {} 

g2(f1); // ok 
g2(f2); // mismatched types … 
g2<int>(f2); // ok 
g2([](int){}); // mismatched types … 
g2<int>([](int){}); // ok 

有沒有一種方法來推斷T不只是簡單的函數指針但對於函子和lambda表達式呢?

還是它必須是這樣的東西?

template<class F> 
void g(F) { typedef first_argument_of<F>::type T; } 

(在我真正的代碼,我需要解構功能有四個參數這種方式,但只std::function::…argument_type存在一個或兩個參數; boost::function有argN_type,但我不認爲我可以無論如何使用,因爲F並不總是function這是我的問題,見上面,等等)

+1

好奇操作從這個'模板'推導'std :: function '爲了調用它? – Yakk

回答

1

讓我們假設我的意見是,OP真的想採取這種變異一個T的函數,並把它變成變異一個std::vector<T>函數讀取,並且認爲,爲了做到這一點,你需要知道什麼T是。

You don't

#include <type_traits> 
#include <utility> 

template<typename Lambda> 
struct container_version { 
    Lambda closure; 
    container_version(container_version const&) = default; 
    container_version(container_version &&) = default; 
    container_version(container_version &) = default; 

    template<typename U> 
    container_version(U&& func):closure(std::forward<U>(func)) {}; 

    // lets make this work on any iterable range: 
    template<typename Container> 
    void operator()(Container&& c) const { 
    for(auto&& x:c) 
     closure(x); 
    } 
}; 

template<typename Lambda> 
container_version< typename std::decay<Lambda>::type > 
make_container_version(Lambda&& closure) { 
    return {std::forward<Lambda>(closure)}; 
} 

#include <vector> 
#include <iostream> 
#include <functional> 
#include <array> 

int main() { 
    std::vector<int> my_vec = {0, 1, 2, 3}; 
    for (auto x:my_vec) 
     std::cout << x << ","; 
    std::cout << "\n"; 
    make_container_version([](int& x) { x++; })(my_vec); 

    for (auto x:my_vec) 
     std::cout << x << ","; 
    std::cout << "\n"; 

    // hey look, we can store it in a `std::function` if we need to: 
    auto super_func = make_container_version([](int& x) { x++; }); 
    std::function< void(std::vector<int>&) > func = super_func; 
    // and the same super_func can be used for a function on a different container: 
    std::function< void(std::array<int,7>&) > func2 = super_func; 

    func(my_vec); 
    for (auto x:my_vec) 
     std::cout << x << ","; 
    std::cout << "\n"; 
} 

事實上,參加論證,使之成爲一個std::function,或迫使其存儲在std::function,成本效率,增加了代碼的複雜性,並使其無法做一些沒有問題的事情。你在哪裏得到你的`T`:

以上版本,然後將其打包成一個std::function,可以set S,list S,vector S,生C陣列,std::array S等

+1

你是對的,我讓它比需要的複雜得多。我並沒有想到包裝本身也可能是一個模板... – pascal

5

有沒有辦法做你想要什麼,由於各種原因。但這裏是一個應該使問題很明顯:

struct function_object 
{ 
    template<typename ...T> 
    void operator()(T&&... v){} 
}; 

f(function_object{}); 

什麼是傳遞給f函數對象的參數的類型?沒有任何東西,它可以用任何種類和數量的參數來調用。

+0

+1;爲了更詳細地闡述,C++不支持由'std :: function'實現的'virtual'樣式多態與'std :: bind'實現的'template'樣式通用多態的組合。 – Potatoswatter

+0

@Patatoswatter:現在是什麼?它肯定是的,或者我根本不理解你在說什麼......你能重述嗎? –

+0

@K在不確定所有模板參數的情況下,不能將模板函數放在'std :: function'中。你可以'std :: bind'一個'std :: function',但結果是一個新的,唯一確定的類型,而不是運行時多態。 – Potatoswatter

2

我也認爲沒有直接的方法來定義單個主模板定義的模板參數和函數參數,以便在所有不同情況下(函數指針,lambda表達式,std::function參數等)都可以推導出T。 。

因此,我會建議您按照問題末尾建議的方法。事實上,無論是std::function還是Boost提供的工具(據我所知)都可以輕鬆實現。

我使用的是什麼(我從過去的其他SO帖子瞭解到)是一個相當複雜的模板function_traits,專門針對所有不同情況。我的定義是這樣的:

template <typename T> 
struct function_traits 
    : public function_traits<decltype(&T::operator())> 
{ }; 

template <typename Return, typename... Args> 
struct function_traits<Return(Args...)> 
{ 
    typedef std::size_t size_type; 
    typedef Return  result_type; 
    typedef result_type function_type(Args...); 

    static constexpr size_type arity = sizeof...(Args); 


    template <size_type index> 
    struct arg 
    { 
    typedef typename std::tuple_element<index,std::tuple<Args...>>::type type; 
    }; 

    static constexpr bool value = true; 
}; 

template <typename Return, typename... Args> 
struct function_traits<Return(*)(Args...)> 
    : function_traits<Return(Args...)> 
{ }; 

template <typename Return, typename... Args> 
struct function_traits<Return(&)(Args...)> 
    : function_traits<Return(Args...)> 
{ }; 

template <typename Class, typename Return, typename... Args> 
struct function_traits<Return(Class::*)(Args...)> 
    : function_traits<Return(Args...)> 
{ }; 

template <typename Class, typename Return, typename... Args> 
struct function_traits<Return(Class::*)(Args...) volatile> 
    : function_traits<Return(Args...)> 
{ }; 

template <typename Class, typename Return, typename... Args> 
struct function_traits<Return(Class::*)(Args...) const> 
    : function_traits<Return(Args...)> 
{ }; 

template <typename Class, typename Return, typename... Args> 
struct function_traits<Return(Class::*)(Args...) const volatile> 
    : function_traits<Return(Args...)> 
{ }; 

要使用這種更加方便,你可能要定義using -ALIASES:

template <typename Fun> 
using result_of = typename function_traits<Fun>::result_type; 

template <typename Fun, std::size_t index> 
using arg = typename function_traits<Fun>::template arg<index>::type; 

所有這些定義(這在下面,我想你把成一個單獨的頭more_type_traits.hpp),就可以很容易定義包裝函數如下:

#include <iostream> 
#include <functional> 
#include <type_traits> 
#include "more_type_traits.hpp" 

template <typename Fun> 
using noref = typename std::remove_reference<Fun>::type; 

template <typename Fun> 
result_of<noref<Fun>> fun(Fun &&argfun) 
{ 
    // Default-initialize the first argument 
    arg<noref<Fun>,0> arg {}; 

    // Call the function 
    return argfun(arg); 
} 

的下面(其基本上是從代碼複製)然後編譯併爲我工作:

void f1(int i) 
{ std::cout << "f1(" << i << ')' << std::endl; } 

struct f2_t 
{ 
    void operator()(int i) 
    { std::cout << "f2(" << i << ')' << std::endl; } 
}; 


int main() 
{ 
    fun(f1); 

    f2_t f2; 
    fun(f2); 

    std::function<void(int)> funobj = [](int i) 
    { std::cout << "funobj(" << i << ')' << std::endl; }; 
    fun(funobj); 

    fun([](int i) { std::cout << "lambda(" << i << ')' << std::endl; }); 

    return 0; 
} 

顯然,function_traits的定義是複雜的,因爲需要很多不同的專業。但是如果你想使函數包裝方便的話,這是值得的。

+0

這個'function_traits'不夠複雜。它不如'std :: result_of'一般[ –

+0

@ R.MartinhoFernandes我沒有說它是完美的。無論如何,哪些部件不見了? – jogojapan

+0

我的觀點並不在於它是否完美,而是它比'std :: result_of'更普遍適用,這導致我「爲什麼我會用這個而不是std :: result_of?」要查看它錯過了什麼,請參閱k-ballo的答案或任何多態函數對象,例如'std :: greater <>'。我沒有看到讓人們的生活方式變得更難以獲得的重點......我不確定是什麼。 –

相關問題