C++ 是否有方法部分匹配可变模板参数包?

C++ 是否有方法部分匹配可变模板参数包?,c++,c++11,c++14,variadic-templates,c++17,C++,C++11,C++14,Variadic Templates,C++17,我目前有一个系统可以将信号“连接”到功能。此信号是一个可变模板,其模板参数为可连接到的函数的参数 在当前的实现中,我显然无法连接到参数与信号的参数不完全相同(或可转换为)的函数。现在,当我试图模拟Qt的信号/插槽/连接,我还想将一个信号的N参数连接到一个插槽的MM信号参数,并将第一个m传递给连接的函数)。有关最简单的代码示例,请参阅 因此,问题有两个方面: 如何使函数的connect调用工作void g(int) 如何使emit调用对函数void g(int)起作用 我猜我必须为插槽及其调用函数

我目前有一个系统可以将信号“连接”到功能。此
信号
是一个可变模板,其模板参数为可连接到的函数的参数

在当前的实现中,我显然无法连接到参数与
信号
的参数不完全相同(或可转换为)的函数。现在,当我试图模拟Qt的
信号
/
插槽
/
连接
,我还想将一个
信号
N
参数连接到一个
插槽
MM
信号参数,并将第一个m传递给连接的函数)。有关最简单的代码示例,请参阅

因此,问题有两个方面:

  • 如何使函数的
    connect
    调用工作
    void g(int)
  • 如何使
    emit
    调用对函数
    void g(int)起作用
  • 我猜我必须为
    插槽及其调用函数制作一些“神奇”的参数包缩减器,但我看不出它们应该如何组合在一起,所以很难真正开始编写解决方案。如果至少Clang/GCC和VisualStudio2015可以编译,我可以使用C++17唯一的解决方案

    以上链接的完整性代码:

    #include <memory>
    #include <vector>
    
    template<typename... ArgTypes>
    struct slot
    {
        virtual ~slot() = default;
    
        virtual void call(ArgTypes...) const = 0;
    };
    
    template<typename Callable, typename... ArgTypes>
    struct callable_slot : slot<ArgTypes...>
    {
        callable_slot(Callable callable) : callable(callable) {}
    
        void call(ArgTypes... args) const override { callable(args...); }
    
        Callable callable;
    };
    
    template<typename... ArgTypes>
    struct signal
    {
        template<typename Callable>
        void connect(Callable callable)
        {
            slots.emplace_back(std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
        }
    
        void emit(ArgTypes... args)
        {
            for(const auto& slot : slots)
            {
                slot->call(args...);
            }
        }
    
        std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
    };
    
    void f(int, char) {}
    
    int main()
    {
        signal<int, char> s;
        s.connect(&f);
    
        s.emit(42, 'c');
    }
    
    #包括
    #包括
    模板
    结构槽
    {
    virtual~slot()=默认值;
    虚拟无效调用(ArgTypes…)const=0;
    };
    模板
    结构可调用\u插槽:插槽
    {
    callable_slot(callable callable):callable(callable){}
    无效调用(ArgTypes…args)常量重写{callable(args…;}
    可调用的可调用的;
    };
    模板
    结构信号
    {
    模板
    void connect(可调用可调用)
    {
    slots.emplace_back(std::make_unique(callable));
    }
    无效发射(ArgTypes…args)
    {
    用于(常量自动和插槽:插槽)
    {
    插槽->呼叫(args…);
    }
    }
    向量时隙;
    };
    void f(int,char){}
    int main()
    {
    信号s;
    s、 连接(&f);
    s、 发射(42,'c');
    }
    

    对于成员指针支持(这需要SFINAE友好的
    std::result\u of
    ),请将
    const\u lvalue\u call\u t
    更改为

    template<class C, class...Args>
    using const_lvalue_call_t = std::result_of_t<const C&(Args&&...)>;
    
    std::ref(c)(std::forward<Args>(args)...); 
    

    这是穷人对左值可调用项的
    std::invoke
    。如果您有C++17,只需直接使用
    std::invoke
    (并使用
    std::void\t
    而不是
    voidify
    ,尽管我喜欢后者的声音)。

    不确定您到底想要什么,但。。。使用
    std::tuple
    std::make_index_sequence

    首先,您需要一个类型traits,它为您提供函数的参数数量(或
    std::function

    然后必须修改
    call()
    方法,将参数打包到
    std::tuple
    中,并调用另一个方法,将tuple和索引序列从0传递到
    numA

    void call(ArgTypes... args) const override
     { callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
    
    下面是一个完整的工作示例

    #include <memory>
    #include <vector>
    #include <iostream>
    #include <functional>
    
    template <typename>
    struct numArgs;
    
    template <typename R, typename ... Args>
    struct numArgs<R(*)(Args...)> 
       : std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename R, typename ... Args>
    struct numArgs<std::function<R(Args...)>>
       : std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename ... ArgTypes>
    struct slot
     {
       virtual ~slot() = default;
    
       virtual void call(ArgTypes...) const = 0;
     };
    
    template <typename Callable, typename ... ArgTypes>
    struct callable_slot : slot<ArgTypes...>
     {
       static constexpr std::size_t numA { numArgs<Callable>::value };
    
       callable_slot(Callable callable) : callable(callable)
        { }
    
       template <std::size_t ... Is>
       void callI (std::tuple<ArgTypes...> const & t,
                   std::index_sequence<Is...> const &) const
        { callable(std::get<Is>(t)...); }
    
       void call(ArgTypes... args) const override
        { callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
    
       Callable callable;
     };
    
    template <typename ... ArgTypes>
    struct signal
     {
       template <typename Callable>
       void connect(Callable callable)
        {
          slots.emplace_back(
             std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
        }
    
       void emit(ArgTypes... args)
        { for(const auto& slot : slots) slot->call(args...); }
    
       std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
     };
    
    void f (int i, char c)
     { std::cout << "--- f(" << i << ", " << c << ")" << std::endl; }
    
    void g (int i)
     { std::cout << "--- g(" << i << ")" << std::endl; }
    
    struct foo
     {
       static void j (int i, char c)
        { std::cout << "--- j(" << i << ", " << c << ")" << std::endl; }
    
       void k (int i)
        { std::cout << "--- k(" << i << ")" << std::endl; }
     };
    
    int main ()
     {
       std::function<void(int, char)> h { [](int i, char c)
           { std::cout << "--- h(" << i << ", " << c << ")" << std::endl; }
        };
    
       std::function<void(int)> i { [](int i)
           { std::cout << "--- i(" << i << ")" << std::endl; }
        };
    
       using std::placeholders::_1;
    
       foo foo_obj{};
    
       std::function<void(int)> k { std::bind(&foo::k, foo_obj, _1) };
    
       signal<int, char> s;
    
       s.connect(f);
       s.connect(g);
       s.connect(h);
       s.connect(i);
       s.connect(foo::j);
       s.connect(k);
    
       s.emit(42, 'c');
     }
    
    #包括
    #包括
    #包括
    #包括
    模板
    结构numArgs;
    模板
    结构numArgs
    :std::积分常数
    { };
    模板
    结构numArgs
    :std::积分常数
    { };
    模板
    结构槽
    {
    virtual~slot()=默认值;
    虚拟无效调用(ArgTypes…)const=0;
    };
    模板
    结构可调用\u插槽:插槽
    {
    静态constexpr std::size\u t numA{numArgs::value};
    可调用\u插槽(可调用):可调用(可调用)
    { }
    模板
    void callI(std::tuple const&t,
    std::index_序列常数&)常数
    {可调用(std::get(t)…);}
    无效调用(ArgTypes…args)常量重写
    {callI(std::make_tuple(args…,std::make_index_sequence{})}
    可调用的可调用的;
    };
    模板
    结构信号
    {
    模板
    void connect(可调用可调用)
    {
    把你放回原处(
    std::使_唯一(可调用));
    }
    无效发射(ArgTypes…args)
    {for(const auto&slot:slots)slot->call(args…)}
    向量时隙;
    };
    空f(整数i,字符c)
    
    {std::cout注意,我希望在任何情况下保持对第一个
    M
    参数的严格类型检查,这样做没有必要取消类型安全性。另一方面,如果我被迫绕过
    虚拟
    将函数调用转发到实际可调用对象并实现“不可能的快速代表"我会考虑这个问题,因为这种类型的东西使我很难理解。在我需要很多额外的代码来处理lambda和成员函数时,这似乎是有限的,或者说我错了吗?@ RuNVB -大量的额外代码?不,我不这么认为。在我看来,这就足以添加<代码> NUMAGS/<代码>专业化,用于<代码> STD::函数
    。真正的问题是lambda函数没有一个真正的类型(任何lambda函数都构成一个类型);因此您需要将它们包装在
    std::function
    s中(这对于结构/类的非静态方法也很有用,与
    std::bind
    );对于结构/类的静态方法,代码工作没有问题。答案针对lambda和方法(带示例)进行了修改。我不想要
    std::function
    的开销,也不想要任何类似的
    std::bind
    。请注意我拥有的
    插槽(完整的东西,不是这个有限的示例)其行为相当于可重置的
    std::function
    (可重置成员函数指针及其调用对象)@rubenvb-我明白了…嗯,如果没有
    std::function
    ,我不知道如何管理lambda;也许其他人可以解决这个问题。这看起来很有希望。我想我可以强制使用参数较少的函数构造可调用的_插槽,然后我就拥有了它。谢谢!嗯,看起来像:(.让我们看看我是否可以强迫它这么做…多谢大家,我让它工作了:。仍然比真实版本的功能要弱一点,但是
    template <typename>
    struct numArgs;
    
    template <typename R, typename ... Args>
    struct numArgs<R(*)(Args...)> 
          : std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename R, typename ... Args>
    struct numArgs<std::function<R(Args...)>>
          : std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    static constexpr std::size_t numA { numArgs<Callable>::value };
    
    void call(ArgTypes... args) const override
     { callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
    
    template <std::size_t ... Is>
    void callI (std::tuple<ArgTypes...> const & t,
                std::index_sequence<Is...> const &) const
     { callable(std::get<Is>(t)...); }
    
    #include <memory>
    #include <vector>
    #include <iostream>
    #include <functional>
    
    template <typename>
    struct numArgs;
    
    template <typename R, typename ... Args>
    struct numArgs<R(*)(Args...)> 
       : std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename R, typename ... Args>
    struct numArgs<std::function<R(Args...)>>
       : std::integral_constant<std::size_t, sizeof...(Args)>
     { };
    
    template <typename ... ArgTypes>
    struct slot
     {
       virtual ~slot() = default;
    
       virtual void call(ArgTypes...) const = 0;
     };
    
    template <typename Callable, typename ... ArgTypes>
    struct callable_slot : slot<ArgTypes...>
     {
       static constexpr std::size_t numA { numArgs<Callable>::value };
    
       callable_slot(Callable callable) : callable(callable)
        { }
    
       template <std::size_t ... Is>
       void callI (std::tuple<ArgTypes...> const & t,
                   std::index_sequence<Is...> const &) const
        { callable(std::get<Is>(t)...); }
    
       void call(ArgTypes... args) const override
        { callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
    
       Callable callable;
     };
    
    template <typename ... ArgTypes>
    struct signal
     {
       template <typename Callable>
       void connect(Callable callable)
        {
          slots.emplace_back(
             std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
        }
    
       void emit(ArgTypes... args)
        { for(const auto& slot : slots) slot->call(args...); }
    
       std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
     };
    
    void f (int i, char c)
     { std::cout << "--- f(" << i << ", " << c << ")" << std::endl; }
    
    void g (int i)
     { std::cout << "--- g(" << i << ")" << std::endl; }
    
    struct foo
     {
       static void j (int i, char c)
        { std::cout << "--- j(" << i << ", " << c << ")" << std::endl; }
    
       void k (int i)
        { std::cout << "--- k(" << i << ")" << std::endl; }
     };
    
    int main ()
     {
       std::function<void(int, char)> h { [](int i, char c)
           { std::cout << "--- h(" << i << ", " << c << ")" << std::endl; }
        };
    
       std::function<void(int)> i { [](int i)
           { std::cout << "--- i(" << i << ")" << std::endl; }
        };
    
       using std::placeholders::_1;
    
       foo foo_obj{};
    
       std::function<void(int)> k { std::bind(&foo::k, foo_obj, _1) };
    
       signal<int, char> s;
    
       s.connect(f);
       s.connect(g);
       s.connect(h);
       s.connect(i);
       s.connect(foo::j);
       s.connect(k);
    
       s.emit(42, 'c');
     }