因此,我尝试应用this blog post中显示的代码,尽管它似乎适用于免费功能,但是当我尝试将其与lambda函数一起使用时,它的外观却平淡无奇。
Here is a fairly minimal demo of the problem.
请注意我设置的constexpr bool
“开关” using_lambda
。设置为false
时,程序将编译并正常运行。设置为true
时,编译器(gcc / c ++ 17)死于以下诊断:
在prog.cc:7包含的文件中:function_traits.h:在实例化中 的“ struct function_traits>”: prog.cc:30:37:从'void EventDispatcher :: Register(F &&) [with F = main()::]'prog.cc:71:10:
从这里需要function_traits.h:47:45:错误:没有命名的类型 使用'struct main()::'中的'type' call_type = function_traits; ^〜function_traits.h:47:45:错误:'结构中没有名为'type'的类型 main()::'function_traits.h:在 'struct function_traits> :: argument <0>'的实例化:prog.cc:31:66:从'void必需 EventDispatcher :: Register(F &&)[with F = main()::]'prog.cc:71:10:从此处开始 function_traits.h:47:45:错误:“结构”中没有名为“类型”的类型 main()::'
恐怕我在这里的深度不够。是否可以使它正常工作,如果可以,怎么办?还是有一种完全不同的方法可以更好地实现相同的目标?
此外,事后想想,我也无法使std::is_invocable
正常工作(请参见第28行)。我怀疑只有当可调用对象对于指定的参数类型可调用 时,它才返回true。我只想检测某些论点是否可以引起人们的注意。如果有人可以阐明如何做到这一点,也将不胜感激。
main.cpp :
// main.cpp
#include <iostream>
#include <unordered_map>
#include <functional>
#include <typeinfo>
#include <typeindex>
#include <type_traits>
#include "function_traits.h"
class Event
{
public:
virtual ~Event()
{}
protected:
Event() = default;
};
class TestEvent : public Event
{};
class EventDispatcher
{
public:
template<class F>
void Register( F&& f )
{
// this static assert seems to always fail
//static_assert(std::is_invocable<F>::value,"Handler must be invocable.");
using Traits = function_traits<F>;
static_assert(Traits::arity == 1,"Handler must have exactly 1 parameter.");
using PCRef = typename Traits::template argument<0>::type;
using PC = typename std::remove_reference<PCRef>::type;
using P = typename std::remove_const<PC>::type;
static_assert(std::is_base_of<Event,P>::value && !std::is_same<Event,P>::value,"Handler parameter must be derived from Event.");
static_assert(std::is_reference<PCRef>::value,"Handler parameter must be passed by reference.");
static_assert(std::is_const<PC>::value,"Handler parameter must be constant reference.");
// wrap f in a lambda that casts to specific event handled
handlers[typeid(P)] = [f]( const Event& e )
{
f( static_cast<const P&>(e) );
};
}
void Dispatch( const Event& e ) const
{
auto i = handlers.find( typeid(e) );
if( i != handlers.end() )
{
i->second( e );
}
}
private:
std::unordered_map<std::type_index,std::function<void(const Event&)>> handlers;
};
void FreeFunctionHandler( [[maybe_unused]] const TestEvent& e )
{
std::cout << "TestEvent fired; handled by free function." << std::endl;
}
int main()
{
constexpr bool using_lambda = true;
EventDispatcher ed;
if constexpr( using_lambda )
{
ed.Register([]( [[maybe_unused]] const TestEvent& e )
{
std::cout << "TestEvent fired; handled by lambda function." << std::endl;
});
}
else
{
ed.Register( FreeFunctionHandler );
}
ed.Dispatch( TestEvent{} );
return 0;
}
function_traits.h :
// function_traits.h
#pragma once
#include <tuple>
template<class F>
struct function_traits;
// function pointer
template<class R,class... Args>
struct function_traits<R( *)(Args...)> : public function_traits<R( Args... )>
{};
template<class R,class... Args>
struct function_traits<R( Args... )>
{
using return_type = R;
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct argument
{
static_assert(N < arity,"error: invalid parameter index.");
using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
};
};
// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};
// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&,Args...)>
{};
// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};
// functor
template<class F>
struct function_traits
{
private:
using call_type = function_traits<decltype(&F::type::operator())>;
public:
using return_type = typename call_type::return_type;
static constexpr std::size_t arity = call_type::arity - 1;
template <std::size_t N>
struct argument
{
static_assert(N < arity,"error: invalid parameter index.");
using type = typename call_type::template argument<N + 1>::type;
};
};
template<class F>
struct function_traits<F&> : public function_traits<F>
{};
template<class F>
struct function_traits<F&&> : public function_traits<F>
{};