什么是 C++ 中的可调用对象?

Posted

技术标签:

【中文标题】什么是 C++ 中的可调用对象?【英文标题】:What is a callable object in C++? 【发布时间】:2013-10-17 04:32:50 【问题描述】:

我目前正在研究提升线程。我发现线程类有一个接受可调用对象的构造函数。什么是可调用对象?

class CallableClass

private:
    // Number of iterations
    int m_iterations;

public:

    // Default constructor
    CallableClass()
    
        m_iterations=10;
    

    // Constructor with number of iterations
    CallableClass(int iterations)
    
        m_iterations=iterations;
    

    // Copy constructor
    CallableClass(const CallableClass& source)
    
        m_iterations=source.m_iterations;
    

    // Destructor
    ~CallableClass()
    
    

    // Assignment operator
    CallableClass& operator = (const CallableClass& source)
    
        m_iterations=source.m_iterations;
        return *this;
    

    // Static function called by thread
    static void StaticFunction()
    
        for (int i=0; i < 10; i++)  // Hard-coded upper limit
        
            cout<<i<<"Do something in parallel (Static function)."<<endl;
            boost::this_thread::yield(); // 'yield' discussed in section 18.6
        
    

    // Operator() called by the thread
    void operator () ()
    
        for (int i=0; i<m_iterations; i++)
        
            cout<<i<<" - Do something in parallel (operator() )."<<endl;
            boost::this_thread::yield(); // 'yield' discussed in section 18.6
        
    

;

这如何成为可调用对象?是因为操作符重载还是构造函数还是其他原因?

【问题讨论】:

因为operator(),是的。见here。此外,此上下文中的“可调用对象”还包括函数、函数指针和 lambda 函数。 这是超载的operator()。它使您可以像调用函数一样调用该类的实例。它们被称为 functorsfunction objectscallable objects 等。 你应该用 boost 标记(和命名)这个问题。 Boost 不是一个标准的 C++ 库,所以如果你正确地标记和命名它,这里非常大的 boost 社区可能能够更快地了解它。 【参考方案1】:

可调用对象是可以像函数一样被调用的东西,语法为object()object(args);即函数指针,或者重载operator()的类类型的对象。

类中operator() 的重载使其可调用。

【讨论】:

【参考方案2】:

这里有两个步骤。在 C++ 标准中,“函数对象”是可以出现在带括号的参数列表左侧的对象,即指向函数的指针或类型具有一个或多个 operator()s 的对象。术语“可调用对象”更广泛:它还包括指向成员的指针(不能用正常的函数调用语法调用)。可调用对象是可以传递给std::bind 等的东西。参见 20.8.1 [func.def] 和 20.8[function.objects]/1。

【讨论】:

【参考方案3】:

可调用对象是来自具有operator() 重载的类的对象实例:

struct Functor 
    ret_t operator()();
    // ...


Functor func;  // func is a callable object

或取消引用的函数指针:

ret_t func() 
   // ...


func;  // func converts to a callable object

【讨论】:

“函数不是对象...(§1.8/1)” @MM.:这就是为什么答案说“一个函数指针”,而不是“一个函数”。也许代码注释可以更精确,例如“func 转换为可调用对象”。 @MikeSeymour:确实 函数指针 是一个对象,但它是不可调用的。事实上,取消引用函数指针是可调用的,IMO。我说的对吗? @MM。 - 函数指针是可调用的:f(3)。不要深入了解是否必须取消引用函数指针的细节。 (*******f)(3),其中f 是一个函数指针,是有效的。【参考方案4】:

至少有一个重载的operator() 的对象是callable object,并且可以调用该运算符及其对象 like 函数调用:

CallableClass obj;
obj();

【讨论】:

我认为“可调用对象”的定义比这更广泛,但我找不到参考。可以肯定的是,带有operator() is 的类是一个可调用对象——我只是不确定,反之亦然。 @JohnDibling:是的,我说的是“一个对象......一个可调用的对象”而不是相反。 @JohnDibling - 你是对的:“可调用对象”包括指向成员的指针。可以用带括号的参数列表调用的东西是函数对象。 @JohnDibling 有效的现代 c++,第一版,五印或标准 c++ 库,第二版,第三印【参考方案5】:

从C++17开始,Callable对象实际上是由标准定义的;详情请见https://en.cppreference.com/w/cpp/named_req/Callable。

【讨论】:

链接坏了:(【参考方案6】:

在 C++11 中,可调用元素可以是:

一个函数指针, 成员函数指针,(与上一个不同,查看here) 仿函数类的对象(实现了 operator() 的类), 匿名函数 (Lambda), 或包含在std::function 对象中的任何上述元素。

这意味着您可以使用每个提到的可调用元素来启动 std::thread。看看下面的示例代码:

std::vector<int> v 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ;

int func()

   return std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());


class A

public:
   int mem_func() 
    
      return std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
   
;

class B

public:
   int operator()()
   
      return std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
   
;

auto lambda = []()  return std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>()); ;


void main()

   A a;
   B b;

   std::function<int()> f1 = &func;
   std::function<int()> f2 = std::bind(&A::mem_func, &a);
   std::function<int()> f3 = std::bind(&B::operator(), &b);
   std::function<int()> f4 = lambda;

   std::thread t1 = std::thread(func);
   std::thread t2 = std::thread(&A::mem_func, a);
   std::thread t3 = std::thread(&B::operator(), b);
   std::thread t4 = std::thread(lambda);

   std::thread t5 = std::thread(f1);
   std::thread t6 = std::thread(f2);
   std::thread t7 = std::thread(f3);
   std::thread t8 = std::thread(f4);

   t1.join();
   t2.join();
   t3.join();
   t4.join();
   t5.join();
   t6.join();
   t7.join();
   t8.join();

【讨论】:

【参考方案7】:

函数对象添加成员函数指针产生所谓的可调用对象。 在c++ 98/03中,我们使用类覆盖operator()作为函数。一般情况下,我们称之为类函数。它具有存储函数状态的优点,其他函数不能。所以它是最重要的概念。还有边框,我们把这种风格的类函数和其他c风格的函数和指向c风格的函数的指针称为“函数对象”。 可调用对象只是“可调用”对象。它包括函数对象和成员函数指针。

【讨论】:

以上是关于什么是 C++ 中的可调用对象?的主要内容,如果未能解决你的问题,请参考以下文章

C++高级开发之可调用对象functionbind

C++高级开发之可调用对象functionbind

C++高级开发之可调用对象functionbind

C++可调用对象学习笔记

C++可调用对象学习笔记

C/C++: C++可调用对象详解