首页 > 解决方案 > 模板元:函数参数类型特征检测

问题描述

因此,我尝试应用此博客文章中显示的代码,虽然它似乎适用于免费函数,但当我尝试将它与 lambda 函数一起使用时,它的表面就变得平淡无奇了。

这是该问题的一个相当简单的演示。

请注意我设置的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: error: no type named 'type' in 'struct main()::' function_traits.h: In instantiation of '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: 错误: 没有命名类型'类型'

恐怕我在这里有点(很多)超出了我的深度。是否有可能让这个工作,如果可以,如何?还是有一种完全不同的方法可以更好地服务于相同的目标?

另外,作为事后的想法,我也无法std::is_invocable正常工作(见第 28 行)。我怀疑它仅在可调用对象对指定的参数类型可调用时才返回 true 。我只想检测是否可以通过任何参数调用某些东西。如果有人可以阐明如何做到这一点,那也将不胜感激。

主.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>
{};

标签: c++template-meta-programming

解决方案


推荐阅读