2017-02-19 149 views
1

我有一個關於將一個類型的std :: function轉換爲另一個具有相同數量的參數或更多的參數的問題,然後調用它,因爲它適用於所有編譯器,但我不確定它是否是已定義的行爲。將std :: function轉換爲不同的std :: function並調用它?

std::function<void(int, float)> func1 = [](int a, float b){ 
    std::cout << a << std::endl; 
    std::cout << b << std::endl; 
}; 

std::function<void(int, float, int, double)>& func2 = 
*reinterpret_cast<std::function<void(int, float, int, double)>*>(&func1); 

func2(1, 2.0f, 3, 4.0); 

這似乎正確地調用func1與期望的參數1,2.0f。其他通過的論據會發生什麼。當我交換func1和func2並在它期待時用2個參數調用它時會發生什麼?4.它是一個明確的行爲,因爲它在msvc,gcc,clang上工作,或者它是某種僥倖,我應該避免它。任何擁有更多專業知識的人都可以詳細闡述這個話題?

+0

什麼是用例?只是好奇。 – Jagannath

+0

這個想法是能夠通過字符串訂閱事件,並且能夠期望來自該事件的相等或更少的參數。這是事件調度程序的代碼: ' –

回答

2

這似乎正確地調用func1 [...]

你不能投了std::function<Sig1>std::function<Sig2>。它們是不相關的類型,儘管它們是同一個函數模板的專業化版本。一個人不能簡單地引用另一個。這是未定義的行爲。未定義行爲的一個潛在後果是代碼似乎工作。然後你改變編譯器。或編譯器版本。或者只是一些隨機的無關代碼,導致優化器做不同的事情。或...

如果你想要一個帶有新簽名的新函數,你必須創建一個新的函數對象。一種方式,如果你想簡單地丟棄最後兩個參數,將是:

std::function<void(int, float, int, double)> func2 = [func1](int a, float b, int, double){ 
    func1(a, b); 
}; 

另一種是採取的事實,即bind僅丟棄未使用的參數:

std::function<void(int, float, int, double)> func2 = std::bind(func1, _1, _2); 

這兩很好。

1

這個想法是能夠通過字符串訂閱事件,並且能夠期待來自該事件的相等或更少的參數。以下是具有可疑功能的事件分派器的代碼。我以爲這會工作,因爲具有恆定大小的std :: function的實現細節以及它的內部緩衝區的工作方式是固定的,並且如果它具有大小或者它存儲指向堆分配存儲器的指針對於捕捉這將解釋爲什麼重新解釋演員工程,但我不確定會發生什麼與額外的參數時調用該函數。

#include <memory> 
#include <string> 
#include <vector> 
#include <unordered_map> 
#include <functional> 

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

template <typename T, typename R, typename... Args> 
struct function_traits<R(T::*)(Args...) const> 
{ 
    typedef R(*pointer)(Args...); 
    typedef R return_type; 
    static constexpr std::size_t arg_count = sizeof...(Args); 
    typedef std::tuple<Args...> args_tuple; 
    typedef const std::function<R(Args...)> function; 
}; 

struct function_wrapper 
{ 
    virtual ~function_wrapper() {} 
    virtual const void* get_ptr() const= 0; 
}; 

template<typename F> 
class function_wrapper_t : public function_wrapper 
{ 
public: 
    function_wrapper_t(F&& f) : _function(f) {} 
    ~function_wrapper_t() {} 
    const void* get_ptr() const { return &_function; } 

private: 
    typename function_traits<F>::function _function; 
}; 

template <typename F> 
std::unique_ptr<function_wrapper> create_wrapper(F f) 
{ 
    return std::unique_ptr<function_wrapper_t<decltype(f)>>(new function_wrapper_t<decltype(f)>(std::forward<F>(f))); 
} 

class event_dispatcher 
{ 
public: 
    template<typename F> 
    void connect(const std::string& name, F f) 
    { 
     static_assert(std::is_same<void, typename function_traits<F>::return_type>::value, 
      "Signals cannot have a return type different from void"); 

     _list[name].emplace_back(create_wrapper(std::forward<F>(f))); 
    } 

    template<typename ... Args> 
    void dispatch(const std::string& name, Args... args) 
    { 
     auto& funcs = _list[name]; 

     for (auto& func : funcs) 
     { 
      auto& f = *reinterpret_cast<const std::function<void(Args...)>*>(func->get_ptr()); 
      f(std::forward<Args>(args) ...); // is this undefined behavior? 
     } 
    } 
private: 
    std::unordered_map<std::string, std::vector<std::unique_ptr<function_wrapper>>> _list; 
}; 

int main() 
{ 
    event_dispatcher d; 
    d.connect("test_event", [](int a, float b) 
    { 

    }); 
    d.connect("test_event", [](int a) 
    { 

    }); 

    d.dispatch("test_event", 1, 2.0f, 3, 4.0); 
} 
相關問題