在 c++-17 中特化的模式匹配中 lambda 的拆分函数签名

Split function signature of lambda in pattern match of specialization in c++-17

本文关键字:拆分 函数 lambda 模式匹配 c++-17      更新时间:2023-10-16

我有以下代码:

#include <iostream>                                                                                                                         
#include <string>                                                                                                                           
#include <array>                                                                                                                            
#include <map>                                                                                                                              
#include <functional>                                                                                                                       
template<typename T> struct tag {};                                                                                                         
template <typename LambdaType, typename=void>                                                                                               
struct split {                                                                                                                              
    split(LambdaType &&f) {                                                                                                                 
        std::cout << "[]()" << std::endl;                                                                                                   
    }                                                                                                                                       
};                                                                                                                                          
template <typename RetType, typename... ArgTypes>                                                                                           
struct split<std::function<RetType(ArgTypes...)>>  {                                                                                        
    split(std::function<RetType(ArgTypes...)> &&f) {                                                                                        
        std::cout << "std::function" << std::endl;                                                                                          
    };                                                                                                                                      
};                                                                                                                                          
template <typename RetType, typename... ArgTypes>                                                                                           
struct split<RetType(*)(ArgTypes...)> {                                                                                                     
    split(RetType(*f)(ArgTypes...)) {                                                                                                       
        std::cout << "func-ptr" << std::endl;                                                                                               
    };                                                                                                                                      
};                                                                                                                                          
void f1(int) {};                                                                                                                            
int                                                                                                                                         
main(int argc, char **argv) {                                                                                                               
    new split<std::decay<decltype(f1)>::type>(f1);                                                                                          
    new split<std::function<void(int)>>(std::function<void(int)>([](int) {}));                                                              
    /* how can I extract the argument type template pack from lambda ? */                                                                   
    new split([](int){});                                                                                                                   
    return 0;                                                                                                                               
}   

split有2个专业,一个std::function<RetType(ArgTypes...)>专业,一个RetType(*)(ArgTypes...)专业。对于这两种专业化,我都得到了RetTypeArgTypes...模板参数,并通过模式匹配打包。但是我想知道天气有没有办法用lambda作为论据来做同样的事情?

如何在new split([](int){})系的专用化中提取 lambda 的RetTypeArgTypes...

您可以将模板类参数推导与std::function一起使用:

template <typename LambdaType, typename=void>                                                                                               
struct split {                                                                                                                              
    using StdFunctionType = decltype(std::function{std::declval<LambdaType>()});                                                                                                                                  
};     

获得与 lambda 对应的std::function后,您可以使用模板专用化检索返回和参数类型。

这是有效的std::function因为有一个扣除指南:

template<class F>
function(F) -> function</*see below*/>;

如果decltype(&F::operator())对于某些类类型G具有R(G::*)(A...)形式(可选cv限定,可选noexcept,可选右值引用限定),则推导类型为std::function<R(A...)>。仅当&F::operator()在被视为未计算的操作数时格式正确时,此重载才会参与重载解析。

你可以经历一些诡计,例如:

#include <type_traits>
template <typename LambdaType, typename=void>                                                                                               
struct split {                                                                                                                              
    split(LambdaType &&f) { deduce(&LambdaType::operator()); }
    template<class RET, class CLOSURE, class... ARGS>
    void deduce(RET(CLOSURE::*)(ARGS...) const) {
       // You have your return and args here
    }
};                                                                                                                                          

template <typename RetType, typename... ArgTypes>                                                                                           
struct split<RetType(*)(ArgTypes...)> {                                                                                                     
    split(RetType(*f)(ArgTypes...));                                                                                                                                      
};                                                                                                                                  
void f1(int) {};                                                                                                                            
int                                                                                                                                         
main(int argc, char **argv) {                                                                                                               
    split<std::decay_t<decltype(f1)>>{f1};                                                                                          
    /* how can I extract the argument type template pack from lambda ? */                                                                   
    split([](int){});                                                                                                                   
    return 0;                                                                                                                               
}   

我找到了一种使用子类化特化的方法(见这里):

/* g++ -std=c++17 */
#include <iostream>
#include <string>
#include <array>
#include <map>
#include <functional>
template<typename T> struct tag {};
struct mybase {};
/* subclass specialization on type of operator() of lambda: */
template<class Ld>
struct split : split<decltype(&Ld::operator())>
{
    split(Ld &&f) : split<decltype(&Ld::operator())>(std::forward<Ld>(f)) {};
};
template <typename RetType, typename... ArgTypes>
struct split<std::function<RetType(ArgTypes...)>>  {
    split(std::function<RetType(ArgTypes...)> &&f) {
        std::cout << "std::function" << std::endl;
    };
};
template <typename RetType, typename... ArgTypes>
struct split<RetType(*)(ArgTypes...)> {
    split(RetType(*f)(ArgTypes...)) {
        std::cout << "func-ptr" << std::endl;
    };
};
template <typename RetType, class Cls, typename... ArgTypes>
struct split<RetType(Cls::*)(ArgTypes...) const >  {
    split(const Cls &&f) {
        std::cout << "[]() const" << std::endl;
    };
};
template <typename RetType, class Cls, typename... ArgTypes>
struct split<RetType(Cls::*)(ArgTypes...) >  {
    split(Cls &&f) {
        std::cout << "[]()" << std::endl;
    };
};

void f1(int) {};
int
main(int argc, char **argv) {
    new split<std::decay<decltype(f1)>::type>(f1);
    new split<std::function<void(int)>>(std::function<void(int)>([](int) {}));
    /* no g++-17: */
    //auto l = [](int){};
    //new split<decltype(l)>(std::forward<decltype(l)>(l));
    /* g++-17: */
    new split([](int){});
    return 0;
}