C++ 可调用对象

2019-11-12 language c/cpp

在 C++11 之前,可调用对象包括了函数、函数指针以及重载 operator() 运算符的对象;而在 C++11 之后,新增了 lambda 表达式以及 bind() 返回值,同时,引入了新的 function 类型,用来统一可调用对象。

简介

在 C++11 之前,可调用对象包括了函数、函数指针以及重载 operator() 运算符的对象;而在 C++11 之后,新增了 lambda 表达式以及 bind() 返回值,同时,引入了新的 function 类型,用来统一可调用对象。

bind() 函数可以把已有的变量绑定到指定函数的参数,从而产生一个新的函数。

如下是一个使用可调用对象的示例。

#include <string>
#include <iostream>
#include <functional>

class Hey {
public:
    void operator() (const std::string &msg) {
        std::cout << "Hey " << msg << "!" << std::endl;
    }
};

void Hi(const std::string &msg)
{
    std::cout << "Hi " << msg << "!" << std::endl;
}

void Message(const std::string &pre, const std::string &msg)
{
    std::cout << pre << msg << "!" << std::endl;
}

int main(void)
{
    Hey hey;
    void (*Hello)(const std::string &);

    /* before C++11 */
    Hi("Andy");                // function
    Hello = Hi, Hello("Andy"); // function pointer
    hey("Andy");               // override operator()

    /* after C++11 */
    std::function<void(const std::string &)> callit;

    callit = Hi;
    callit = hey;
    callit = [](const std::string &msg) {
            std::cout << "Hi " << msg << "!" << std::endl;
    };
    callit = std::bind(Message, "Hello ", std::placeholders::_1);
    callit("Bruce");

    return 0;
}

ref cref

在 C++ 语法中,已经存在了引用,那么为什么在 C++11 中还要引入 std::ref 呢?

其实,主要是考虑到函数式编程 (例如 std::bind() ) ,默认是对参数的直接拷贝而非引用,通过 std::refstd::cref 可以分别表示引用以及 const 引用。

#include <iostream>
#include <functional>

void foobar(int &a, int &b, const int &c)
{
    std::cout << "foobar " << a << ", " << b << ", " << c << std::endl;
    ++a; // increments the copy of a stored in the function object
    ++b; // increments the main()'s variable.
    // ++c; // compile error
}

int main(void)
{
    int a = 1, b = 2, c = 3;
    std::function<void()> func = std::bind(foobar, a, std::ref(b), std::cref(c));
    func();
    std::cout << " After " << a << ", " << b << ", " << c << std::endl;
    return 0;
}

其输出的结果如下。

foobar 1, 2, 3
 After 1, 3, 3

也就是说,只有变量 b 在被调用的函数中被修改了。