代码之家  ›  专栏  ›  技术社区  ›  Klaus

从函数指针类型间接推断的转发参数

  •  1
  • Klaus  · 技术社区  · 6 年前

    我想要一个函数,它接受一个指向函数的指针,并转发函数指针类型本身给定的所有参数,如下所示:

    template < typename RET, typename ... ARGS >
    auto Do1( RET(*ptr)(ARGS...), ARGS... args )
    {
        (*ptr)(std::forward<ARGS>( args )...);
    }
    
    int main ()
    {
        int i=4;
    
        Do1( &Ex1, i );
        Do1( &Ex2, i ); //fails!
        Do1( &Ex3, i+1 ); // fails
    }
    

    要调用的函数用于这两个示例:

    void Ex1( int i){ std::cout << __PRETTY_FUNCTION__ << " " << i << std::endl; i=10;}
    void Ex2( int& i){ std::cout << __PRETTY_FUNCTION__ << " " << i << std::endl; i=20;}
    void Ex3( int&& i){ std::cout << __PRETTY_FUNCTION__ << " " << i << std::endl; i=30;}
    

    如果发生故障 Ex2 Ex3 简单地说,当它尝试两次推导参数列表的类型时,结果是不同的。编译器抱怨:

    main.cpp:57:22: error: no matching function for call to 'Do1(void (*)(int&), int&)'
             Do1( &Ex2, i ); //fails!
                          ^   
    main.cpp:33:10: note: candidate: 'template<class RET, class ... ARGS> auto Do1(RET (*)(ARGS ...), ARGS ...)'
         auto Do1( RET(*ptr)(ARGS...), ARGS... args )
              ^~~ 
    main.cpp:33:10: note:   template argument deduction/substitution failed:
    main.cpp:57:22: note:   inconsistent parameter pack deduction with 'int&' and 'int'
    

    之后,我尝试使用以下方法来解决问题,因为我只提取了一次类型,推导出args列表,然后再次转发到中间lambda,如下所示:

    template < typename RET, typename ... ARGS >
    auto Do2( RET(*ptr)(ARGS...) )
    {   
        return [ptr]( ARGS ... args )
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
            (*ptr)(std::forward<ARGS>(args)...); 
        };
    }   
    
    int main ()
    {   
        int i=4;
    
        Do1( &Ex1, i );
        Do1( &Ex2, i ); //fails!
        Do1( &Ex3, i+1 ); // fails
    
        Do2( &Ex1 )( i );
        std::cout << "now i: " << i << std::endl;
        std::cout << std::endl;
    
        Do2( &Ex2 )( i );
        std::cout << "now i: " << i << std::endl;
        std::cout << std::endl;
    
        Do2( &Ex3 )( i+1 );
        std::cout << "now i: " << i << std::endl;
        std::cout << std::endl;
    }
    

    问:在任何情况下,有没有办法修复第一种方法来消除中间lambda?如果不是,中间lambda的解决方案是否设计得“很好”,特别是所有“转发”的东西,这样我就不会创建一些副本或其他意想不到的行为?

    编辑: 这只是一个简化的例子。我不是要写一份 std::invoke . 所以在我的现实世界中有很多代码需要在 Do 方法本身。

    从函数指针类型获取所需类型很重要,因为我必须在内部执行一些检查 与函数指针提供的类型相关,并且 从给定的附加参数中,我提供了从用户代码到 方法。

    2 回复  |  直到 6 年前
        1
  •  3
  •   max66    6 年前

    问:在任何情况下,有没有办法修复第一种方法来消除中间lambda?

    我建议接受可调用的 ptr 简单地作为一种类型

    template < typename F, typename ... ARGS >
    auto Do1( F func, ARGS && ... args )
     {
        func(std::forward<ARGS>( args )...);
     }
    

    这样你 Do1() 完全避免双重不同的演绎问题,也适用于其他类型的可调用(例如:使用不能简单转换为函数指针的泛型lambda)。

    否则,可以截取两个变元参数类型列表

    template < typename RET, typename ... AS1, typename ... AS2 >
    auto Do1( RET(*ptr)(AS1...), AS2 && ... args )
     {
       (*ptr)(std::forward<AS2>( args )...);
     }
    
        2
  •  3
  •   Jarod42    6 年前

    问:在任何情况下,有没有办法修复第一种方法来消除中间lambda?

    您可以将第二个参数更改为不可推导:

    template <typename T>
    struct non_deducible {
        using type = T;  
    };
    template <typename T>
    using non_deducible_t = typename non_deducible<T>::type;
    
    template < typename RET, typename ... ARGS >
    auto Do1( RET(*ptr)(ARGS...), non_deducible_t<ARGS>... args );
    

    Demo

    中间lambda的解决方案是否设计得“很好”,特别是所有的“转发”功能,以便我不会创建一些副本或其他意外行为?

    你做了额外的移动构造,所以 void Ex1(std::array<int, 5>) ,你复制两次 std::array . 解决方案正在转发参考:

    template < typename RET, typename ... ARGS >
    auto Do2( RET(*ptr)(ARGS...) )
    {   
        return [ptr](auto&& ... args )
        -> decltype((*ptr)((decltype(args)(args))...))
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
            (*ptr)((decltype(args)(args))...); 
        };
    }
    

    简单的替代方案是:

    template < typename Ret, typename ... Ts, typename ... Args >
    auto Do1( Ret(*ptr)(Ts...), Args&& ... args)
    {
        (*ptr)(std::forward<Args>(args)...);
    }
    

    甚至

    template < typename Func, typename ... Args >
    auto Do1(Func f, Args&& ... args)
    {
        f(std::forward<Args>(args)...);
    }
    

    你可能还有一些 function_traits 检查 Func .