Arity of a generic lambda

前端 未结 5 1235
说谎
说谎 2020-12-01 23:53

It is possible to deduce arity of a non-generic lambda by accessing its operator().

template 
struct fInfo : fInfo

        
5条回答
  •  有刺的猬
    2020-12-02 00:34

    This technique will work in some cases. I create a fake_anything type that can fake almost anything, and try to invoke your lambda with some number of instances of that.

    #include 
    
    struct fake_anything {
      fake_anything(fake_anything const&);
      fake_anything();
      fake_anything&operator=(fake_anything const&);
      templateoperator T&() const;
      templateoperator T&&() const;
      templateoperator T const&() const;
      templateoperator T const&&() const;
      fake_anything operator*() const;
      fake_anything operator++() const;
      fake_anything operator++(int) const;
      fake_anything operator->() const;
      templatefake_anything(T&&);
    };
    fake_anything operator+(fake_anything, fake_anything);
    fake_anything operator-(fake_anything, fake_anything);
    fake_anything operator*(fake_anything, fake_anything);
    fake_anything operator/(fake_anything, fake_anything);
    // etc for every operator
    
    templateusing void_t=void;
    template
    struct can_invoke:std::false_type{};
    template
    struct can_invoke()( std::declval()... ) ) >
    > : std::true_type
    {};
    
    templatestruct is_sig:std::false_type{};
    templatestruct is_sig:std::true_type{};
    
    templatestruct indexes{using type=indexes;};
    templatestruct make_indexes:make_indexes{};
    templatestruct make_indexes<0,Is...>:indexes{};
    templateusing make_indexes_t=typename make_indexes::type;
    
    templateusing unpacker=T;
    
    template
    struct nary_help;
    template
    struct nary_help>:
      can_invoke... )>
    {};
    template
    struct has_n_arity:
      nary_help>
    {};
    
    template
    struct max_arity{
      enum{Mid=(Max+Min)/2};
      enum{
        lhs = max_arity::value,
        rhs = max_arity::value,
        value = lhs>rhs?lhs:rhs,
      };
    };
    template
    struct max_arity:
      std::integral_constant::value?(int)X:-1>
    {};
    
    template
    struct min_arity{
      enum{Mid=(Max+Min)/2};
      enum{
        lhs = min_arity::value,
        rhs = min_arity::value,
        value = lhs
    struct min_arity:
      std::integral_constant::value?X:(unsigned)-1>
    {};
    
    auto test1 = [](auto x, auto y)->bool { return x < y; };
    auto test2 = [](auto x, auto y) { return x + y; };
    auto test3 = [](auto x) { return x.y; };
    
    int main() {
      std::cout << can_invoke< decltype(test1)( fake_anything, fake_anything ) >::value << "\n";
      std::cout << can_invoke< decltype(test1)( int, int ) >::value << "\n";
      std::cout << has_n_arity< decltype(test1), 2 >::value << "\n";
      std::cout << max_arity< decltype(test1) >::value << "\n";
      std::cout << max_arity< decltype(test2) >::value << "\n";
      // will fail to compile:
      // std::cout << max_arity< decltype(test3) >::value << "\n";
    }
    

    live example.

    Note sufficient SFINAE will mean the above will get the wrong result, as will use of operator., or use of operator. on certain kinds of "derived" types, or accessing types based off of the fake_anything parameter, etc.

    However, if the lambda specifies its return value with a ->X clause, then fake_anything is more than good enough. The hard part is dealing with the body.

    Note that this approach is often a bad idea, because if you want to know the arity of a function, you probably also know the types of the things you want to invoke the function object with! And above I answer that question really easily (can this function object be invoked with these arguments?). It can even be improved to ask "what is the longest/shortest prefix of these arguments that can invoke this function object", or handle "how many repeats of type X work to invoke this function object" (if you want clean failure, you need an upper bound).

提交回复
热议问题