2017-02-19 69 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); 
}