C++Lambda没有运算符()

C++ Lambda does not have operator()

本文关键字:运算符 C++Lambda      更新时间:2023-10-16

我需要一种计算函数参数类型的方法,因此我编写了一个closure_traits类,如下所示,灵感来自于是否可以计算lambda的参数类型和返回类型?。

但是,当我尝试将其应用于简单lambda时,我会得到一个错误,即"operator(("不是"(lambda类型("的成员。但是,根据cppreference,lambda确实有一个运算符((。我还尝试使用std::函数,得到了相同的错误。我想我不确定出了什么问题,如果有任何帮助,我们将不胜感激。

#include<type_traits> 
#include<tuple>                                                                           
#include<utility> 
#include<iostream>                                                                                                     
/* For generic types use the type signature of their operator() */                                                     
template <typename T>                                                                                      
struct closure_traits : public
                        closure_traits<decltype(&T::operator())> {};                                        
/* Otherwise, we do a template match on a function type. */
template <typename ClassType, typename ReturnType, 
          typename... ArgTypes>                                                  
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>                                                  
{
    using arity = std::integral_constant<std::size_t,
                                         sizeof...(ArgTypes)>;                                              
    using Ret = ReturnType;                                  
    /* The argument types will be the same as the types of the 
     * elements of a tuple composed of them. 
     */                                                                                             
    template <std::size_t I>       
    struct Args {        
        using type = typename std::tuple_element<I, 
                                       std::tuple<ArgTypes...>>::type;                                                                                                                     
    };                                                                                                                                                                                                                                        
};                                                                                                                                                                                                                                                
int main() {                                                                                                                     
    auto thing = [=] (int x) {return x;}; 
    std::cerr << "The number of arguments is " 
              << closure_traits<decltype(thing)>::arity << std::endl;                                                                                                                     
    return 0;                                                                                                                       
}    

我收到的编译器错误消息如下。我的编译命令只是g++-std=c++14main.cpp.

main.cpp: In instantiation of ‘struct closure_traits<int (main()::<lambda(int)>::*)(int) const>’:
main.cpp:9:8:   required from ‘struct closure_traits<main()::<lambda(int)> >’
main.cpp:34:82:   required from here
main.cpp:9:56: error: ‘operator()’ is not a member of ‘int (main()::<lambda(int)>::*)(int) const’
 struct closure_traits : public closure_traits<decltype(&T::operator())> {};                                        
                                                    ^
main.cpp: In function ‘int main()’:
main.cpp:34:51: error: ‘arity’ is not a member of ‘closure_traits<main()::<lambda(int)> >’
 std::cerr << "The number of arguments is " << closure_traits<decltype(thing)>::arity << std::endl; 

您的专业化与decltype(&T::operator())参数不匹配。

正因为如此,编译器被迫递归地选择相同的主模板,而不是选择专门化(按照您的意愿(。这使得它在已经应用了一次&T::operator()表达式之后再次应用它。I.e最初尝试执行&T::operator()实际上成功了,但是当T已经是int (main()::<lambda(int)>::*)(int) const时,编译器尝试再次应用&T::operator()。后者显然没有operator (),这就是您收到此错误消息的原因。

它无法选择您的专业化的原因是模板参数声明中缺少const。lambda的operator ()实际上是lambda类的const成员。将const添加到您的专业化声明中

template <typename ClassType, typename ReturnType, 
          typename... ArgTypes>                                                  
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>   
...

编译器将遵循您希望它遵循的专用化路径。

当然,您必须打印closure_traits<decltype(thing)>::arity::value,而不仅仅是closure_traits<decltype(thing)>::arity

编译器正确地获取lambda类型的operator(),但由于const限定符,指向该成员函数的指针与您的专业化不匹配。

你应该添加第二个专业化

template <typename ClassType, typename ReturnType, 
          typename... ArgTypes>                                                  
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>                                                  
{
    // ...
};

(是的,编写采用函数类型的模板是很痛苦的。(

如果有人对此有问题,正确的代码如下:

#include<type_traits> 
#include<tuple>                                                                           
#include<utility> 
#include<iostream>                                                                                                     
/* For generic types use the type signature of their operator() */                                                     
template <typename T>                                                                                      
struct closure_traits : public
                        closure_traits<decltype(&T::operator())> {};                                        
/* Otherwise, we do a template match on a function type. */
template <typename ClassType, typename ReturnType, 
          typename... ArgTypes>                                                  
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>                                                  
{
    using arity = std::integral_constant<std::size_t,
                                         sizeof...(ArgTypes)>;                                              
    using Ret = ReturnType;                                  
    /* The argument types will be the same as the types of the 
     * elements of a tuple composed of them. 
     */                                                                                             
    template <std::size_t I>       
    struct Args {        
        using type = typename std::tuple_element<I, 
                                       std::tuple<ArgTypes...>>::type;                                                                                                                     
    };                                                                                                                                                                                                                                        
};                                                                                                                                                                                                                                                
int main() {                                                                                                                     
    auto thing = [=] (int x) {return x;}; 
    std::cerr << "The number of arguments is " 
              << closure_traits<decltype(thing)>::arity::value << std::endl;                                                                                                                     
    return 0;                                                                                                                       
}    

EDIT:评论中(正确地(指出,这不涉及except说明符和某些cv资格情况。如果你需要的话,你可能需要添加这些案例,或者查看评论中发布的链接。


如果你正在为c++中所有可以调用的类型寻找一个(更(完整的解决方案,那么这些答案中的很多都是有效的,但却错过了一些关键的情况,比如

  • 对lambda的引用
  • 函数和函数指针

以下是我所知的完整解决方案(如果有任何遗漏,请在评论中告诉我(:

template <typename>
struct closure_traits;
template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
    : closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};
template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
    using arguments = std::tuple<Args...>;
    static constexpr std::size_t arity = std::tuple_size<arguments>::value;
    template <std::size_t N>
    using argument_type = typename std::tuple_element<N, arguments>::type;
    using return_type = ReturnTypeT;
};
template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
    : closure_traits<ReturnTypeT(Args...)>
{
};
// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
    : closure_traits<ReturnTypeT(Args...)>
{
    using class_type = ClassTypeT;
};
// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
    : closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};

免责声明:std::remove_reference的灵感来源于此代码。