0

在試圖實現一個委託 -class使用可變參數模板我遇到了一個問題,我無法來解決:C++ 11 - 使用decltype函數指針有什麼問題?

/// -------------------------------------- 
/// @thanks  God 
///    Steve Reinalter 
/// @author  Henri Korpela aka Helixirr 
/// -------------------------------------- 
#include <cstdio> 

template<typename> 
class Delegate; 

template<typename Return, typename Param, typename... ParamsOther> 
class Delegate<Return (Param, ParamsOther...)>{ 
public: 

    /// Constructors & destructors: 
    Delegate(void) = default; 
    Delegate(Delegate const& delegate_) = default; 
    Delegate(Delegate&& delegate_) = default; 

    /// Member functions: 
    Delegate& bind(Return (*function_)(Param, ParamsOther...)); 
    template<class C> 
    Delegate& bind(C& c_, Return (C::*function_)(Param, ParamsOther...)); 

    /// Member functions (overloaded operators, assignment): 
    Delegate& operator=(Delegate const& delegate_) = default; 
    Delegate& operator=(Delegate&& delegate_) = default; 

    /// Member functions (overloaded operators, function call): 
    inline Return operator()(Param param_, ParamsOther... params_other_) const; 

private: 
    /// Member data: 
    Return (*_m_opFunction)(Param, ParamsOther...) = nullptr; 
    void* _m_opInstance = nullptr; 
    /// Static member functions: 
    template<class C, Return (C::*Function)(Param, ParamsOther...)> 
    static inline Return _wrap_function_member(void* instance_, Param param_, ParamsOther... params_other_); 
    template<Return (*Function)(Param, ParamsOther...)> 
    static inline Return _wrap_function_static(void*, Param param_, ParamsOther... params_other_); 
}; 

/// Member functions: 
template<typename Return, typename Param, typename... ParamsOther> 
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(Return (*function_)(Param, ParamsOther...)){ 
    _m_opFunction = &_wrap_function_static<decltype(function_)>; 
    _m_opInstance = nullptr; 
    return *this; 
} 
template<typename Return, typename Param, typename... ParamsOther> 
template<class C> 
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(C& c_, Return (C::*function_)(Param, ParamsOther...)){ 
    _m_opFunction = &_wrap_function_member<C, decltype(function_)>; 
    _m_opInstance = &c_; 
    return *this; 
} 
/// Member functions (overloaded operators, function call): 
template<typename Return, typename Param, typename... ParamsOther> 
Return Delegate<Return (Param, ParamsOther...)>::operator()(Param param_, ParamsOther... params_other_) const{ 
    return _m_opFunction(_m_opInstance, param_, params_other_...); 
} 
/// Static member functions: 
template<typename Return, typename Param, typename... ParamsOther> 
template<class C, Return (C::*Function)(Param, ParamsOther...)> 
Return Delegate<Return (Param, ParamsOther...)>::_wrap_function_member(void* instance_, Param param_, ParamsOther... params_other_){ 
    return (static_cast<C*>(instance_)->*Function)(param_, params_other_...); 
} 
template<typename Return, typename Param, typename... ParamsOther> 
template<Return (*Function)(Param, ParamsOther...)> 
Return Delegate<Return (Param, ParamsOther...)>::_wrap_function_static(void*, Param param_, ParamsOther... params_other_){ 
    return (Function)(param_, params_other_...); 
} 

int f(int i_){ 
    return i_ * 2; 
} 

int main(void){ 
    Delegate<int (int)> delegate__; 
    delegate__.bind(&f); 
    printf("Result: %i\n", delegate__(8)); 
    return 0; 
} 

我試着用C++ 11編譯器來編譯在IdeoneGCC 4.7.2),但似乎失敗:

prog.cpp: In instantiation of ‘Delegate& Delegate::bind(Return (*)(Param, ParamsOther ...)) [with Return = int; Param = int; ParamsOther = {}]’: prog.cpp:79:23: required from here prog.cpp:45:5: error: no matches converting function ‘_wrap_function_static’ to type ‘int (*)(int)’ prog.cpp:39:26: error: candidate is: template static Return Delegate::_wrap_function_static(void*, Param, ParamsOther ...) [with Return (* Function)(Param, ParamsOther ...) = Function; Return = int; Param = int; ParamsOther = {}] prog.cpp: In instantiation of ‘Return Delegate::operator()(Param, ParamsOther ...) const [with Return = int; Param = int; ParamsOther = {}]’: prog.cpp:80:40: required from here prog.cpp:59:65: error: invalid conversion from ‘void*’ to ‘int’ [-fpermissive] prog.cpp:59:65: error: too many arguments to function

從我可以理解,decltype樂趣ction指針這裏

template<typename Return, typename Param, typename... ParamsOther> 
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(Return (*function_)(Param, ParamsOther...)){ 
    _m_opFunction = &_wrap_function_static<decltype(function_)>; 
    _m_opInstance = nullptr; 
    return *this; 
} 

似乎是造成問題的原因。當我嘗試將成員函數綁定到委託時會發生同樣的情況。這是爲什麼?我究竟做錯了什麼?對我來說,獲取函數指針的類型並將該類型用作模板參數似乎很自然,但出於某種原因,它在這裏不起作用。 這種decltype和函數指針場景有什麼問題?

+0

這是完全不清楚你正在嘗試做什麼。您將函數指針傳遞給'bind',但'bind'從不使用其參數的值。你是否計劃稍後能夠調用傳遞的函數? – 2013-05-07 20:23:36

+0

'template static inline返回_wrap_function_static /*...*/'期待函數指針作爲非類型模板參數。 '&_wrap_function_static '提供了一個函數指針類型作爲模板參數。 – dyp 2013-05-07 20:46:03

+0

哦,順便說一句。爲什麼不使用'std :: bind'和'std :: function'? – dyp 2013-05-07 20:47:20

回答

1

下面是從鐺錯誤消息3.2 ++第一部分:

temp.cpp:41:19: error: assigning to 'int (*)(int)' from incompatible type 
     '<overloaded function type>' 
    _m_opFunction = &_wrap_function_static<decltype(function_)>; 
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
temp.cpp:75:16: note: in instantiation of member function 'Delegate<int 
     (int)>::bind' requested here 
    delegate__.bind(&f); 
      ^
temp.cpp:35:26: note: candidate function has different number of parameters 
     (expected 1 but has 2) 
    static inline Return _wrap_function_static(void*, Param param_,... 
         ^


temp.cpp:55:41: error: too many arguments to function call, expected 1, have 2 
    return _m_opFunction(_m_opInstance, param_, params_other_...); 
      ~~~~~~~~~~~~~    ^~~~~~ 
temp.cpp:76:38: note: in instantiation of member function 'Delegate<int 
     (int)>::operator()' requested here 
    printf("Result: %i\n", delegate__(8)); 

這是因爲的_m_opFunction聲明的:

Return (*_m_opFunction)(Param, ParamsOther...) = nullptr; 

template<Return (*Function)(Param, ParamsOther...)> 
static inline Return _wrap_function_static(void*, Param param_, ParamsOther... params_other_); 

即,_wrap_function_static期望一個void*則參數轉發給函數調用,而_m_opFunction只需要函數調用的參數。


標準庫解決方案:

#include <iostream> 
#include <functional> 

int f(int i_){ 
    return i_ * 2; 
} 

struct foo 
{ 
    int m; 
    int f(int i) { return i * m; } 
}; 

int main() 
{ 
    std::function<int (int)> delegate__; 

    delegate__ = f; 
    std::cout << "Result: " << delegate__(8) << std::endl; 

    foo o; 
    o.m = 21; 
    delegate__ = std::bind(&foo::f, std::ref(o), std::placeholders::_1); 
    std::cout << "Result: " << delegate__(2) << std::endl; 
} 

試圖修復你的方法: 注:不能從一個成員函數指針轉換爲一個「普通」的函數指針(它可能工作使用工會或複製原始數據...... UB)。更好的方法是使用多態性(即虛擬功能&動態分配調用者對象)。

#include <cstdio> 

template<typename> 
class Delegate; 

template<typename Return, typename Param, typename... ParamsOther> 
class Delegate<Return (Param, ParamsOther...)>{ 
public: 

    /// Constructors & destructors: 
    Delegate(void) = default; 
    Delegate(Delegate const& delegate_) = default; 
    Delegate(Delegate&& delegate_) = default; 

    /// Member functions: 
    Delegate& bind(Return (*function_)(Param, ParamsOther...)); 
    template<class C> 
    Delegate& bind(C& c_, Return (C::*function_)(Param, ParamsOther...)); 

    /// Member functions (overloaded operators, assignment): 
    Delegate& operator=(Delegate const& delegate_) = default; 
    Delegate& operator=(Delegate&& delegate_) = default; 

    /// Member functions (overloaded operators, function call): 
    inline Return operator()(Param param_, ParamsOther... params_other_) const; 

private: 
    /// Member data: 
    Return (*_m_opFunction)(Param, ParamsOther...) = nullptr; 
    Return (Delegate::*_m_opMemFunction)(Param, ParamsOther...) = nullptr; 
    void* _m_opInstance = nullptr; 

    /// function wrappers: 
    template<class C> 
    static inline Return _wrap_member_function(Delegate const&, Param param_, ParamsOther... params_other_); 
}; 

/// Member functions: 
template<typename Return, typename Param, typename... ParamsOther> 
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(Return (*function_)(Param, ParamsOther...)){ 
    _m_opFunction = function_; 
    _m_opMemFunction = nullptr; 
    _m_opInstance = nullptr; 
    return *this; 
} 
template<typename Return, typename Param, typename... ParamsOther> 
template<class C> 
Delegate<Return (Param, ParamsOther...)>& Delegate<Return (Param, ParamsOther...)>::bind(C& c_, Return (C::*function_)(Param, ParamsOther...)){ 
    _m_opFunction = reinterpret_cast<decltype(_m_opFunction)>(&_wrap_member_function<C>); 
    _m_opMemFunction = reinterpret_cast<decltype(_m_opMemFunction)>(function_); 
    _m_opInstance = &c_; 
    return *this; 
} 
/// Member functions (overloaded operators, function call): 
template<typename Return, typename Param, typename... ParamsOther> 
Return Delegate<Return (Param, ParamsOther...)>::operator()(Param param_, ParamsOther... params_other_) const{ 
    if(nullptr == _m_opMemFunction) 
    { 
     return _m_opFunction(param_, params_other_...); 
    }else 
    { 
     auto f = reinterpret_cast<Return (*)(Delegate const&, Param, ParamsOther...)>(_m_opFunction); 
     return f(*this, param_, params_other_...); 
    } 
} 
/// function wrappers: 
template<typename Return, typename Param, typename... ParamsOther> 
template<class C> 
Return Delegate<Return (Param, ParamsOther...)>::_wrap_member_function(Delegate<Return (Param, ParamsOther...)> const& instance_, Param param_, ParamsOther... params_other_){ 
    Return (C::*memFuncPtr)(Param, ParamsOther...) = reinterpret_cast<decltype(memFuncPtr)>(instance_._m_opMemFunction); 
    return (reinterpret_cast<C*>(instance_._m_opInstance)->*memFuncPtr)(param_, params_other_...); 
} 

int f(int i_){ 
    return i_ * 2; 
} 

struct foo 
{ 
    int m; 
    int f(int i) { return i * m; } 
}; 

int main(void){ 
    Delegate<int (int)> delegate__; 
    delegate__.bind(&f); 
    printf("Result: %i\n", delegate__(8)); 

    foo o; 
    o.m = 21; 
    delegate__.bind(o, &foo::f); 
    printf("Resilt: %i\n", delegate__(2)); 
    return 0; 
} 

爲什麼我說 「的設計可以說是硬傷」:

  • 標準庫已經包含了 「委託」 類型的參數(std::function
    • 訂單是成員不同StdLib綁定函數(std::bind(&A::func, instance) vs delegate__.bind(instance, &A::func)
    • StdLib綁定複製實例,您有t Ø明確使用std::ref(或指針)傳遞一個參考< - 容易出錯少,因爲很明顯,你必須保持實例的生命,直到被調用的函數
  • 函數調用拷貝參數(最好:完美轉發)
  • 爲什麼要拆分參數ParamParamOthers
  • 與仿函數不兼容
+0

非常感謝,我現在看到了。但是,我沒有已經做過默認的初始化函數指針爲0嗎?像「Delegate(void)= default;」?我也初始化了fptrs在類範圍中,我是否遺漏了一些東西?好吧,無論如何,這裏有大量的強制轉換,但是強制轉換基本上就是這樣。我喜歡std :: function&std :: bind示例,幫助我比較不同的儘管我認爲我的語法更加用戶友好,但是必須添加函數和lambda支持,我會看看它是如何發生的,如果出現問題,您可能會再次發現我在這裏再次提出問題。 D – Helixirr 2013-05-08 14:06:59

+0

@Helixirr如果你不喜歡它們,你可以包裝'std :: function'和'std :: bind'句法。 fct ptrs的初始化:[class.ctor]/6「隱式定義的默認構造函數執行該類的初始化設置,該設置將由用戶編寫的該類的默認構造函數執行,且沒有ctor-initializer」對於明確違約的ctor)非靜態數據成員不帶括號或等號初始值設定項,也不會在ctor-initializer中出現的數據成員將是default-init;但是您需要value-init( - > zero-init)將fct ptrs設置爲'nullptr'。 – dyp 2013-05-08 14:17:14

+0

我想知道爲什麼函數指針初始化就是這樣。如果它能按我的方式工作,它會更有意義嗎?是的,感謝提示,包裝可能是一個好主意。 – Helixirr 2013-05-08 14:52:59