在 std::for_each 和 std::transform 中正确使用函子

Proper use of a functor in std::for_each and std::transform

这可能是一个幼稚的问题,但无论如何:

假设我有一个像这样的 class :

class func {
    public:
        int operator()(int it) const { return it + 21; }
};

很明显,上面定义了一个仿函数 class,如果我这样做:

func obj;
int capture = obj(9);
cout << capture << endl;

很明显,结果将是 30。但是假设我使用 STL std::transform 根据上面定义的仿函数使用另一个容器的值来转换一个容器:

vector<int> v, vi;
v.push_back(1);
v.push_back(2);
vi.resize(v.size());

我遵循以下语法,其中我直接使用 class-name 调用仿函数,而且,没有参数传递给仿函数(根据定义它需要):

std::transform(v.begin(), v.end(), vi.begin(), func());

这非常有效。为什么会这样?尽管没有使用 func 的实例并且也没有传递参数(这显然是第一个容器的元素),但为什么这样做有效?

此外,如果我像上面那样使用带有参数的 func 实例,则会导致编译错误。

 func instance;
 std::transform(v.begin(), v.end(), vi.begin(), instance());

如何在std::transform/std::for_each中正确使用函子?为什么调用仿函数方法的方式不同?

此外,从this answer on functors,我们有以下代码:

// this is a functor
struct add_x {
  add_x(int x) : x(x) {}
  int operator()(int y) const { return x + y; }

  private:
    int x;
};

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1));

答案说add_x(1)在这里充当了一个仿函数(而不是一个实例),它在我上面的例子中是一个实例吗?

std::transform(v.begin(), v.end(), vi.begin(), lambda_function());
//                                             ^^^^^^^^^^^^^^^^^

这不是调用 lambda_function::operator(),它只是创建 lambda_function 的临时实例。在 std::transform 内,此对象将使用 v 的内容作为参数迭代调用其 operator()

如果使用 C++11 的大括号初始化,会发生什么更明显:

std::transform(v.begin(), v.end(), vi.begin(), lambda_function{});

或者如果你考虑这个:

lambda_function()(0);
//             ^^    creates instance
//               ^^^ calls operator()

关于你的第二个例子:

std::transform(in.begin(), in.end(), out.begin(), add_x(1));
//                                                ^^^^^^^^

这将再次创建 add_x 的临时实例,但它不会调用默认构造函数,而是调用 add_x(int x); 构造函数。这会初始化函数对象中的某些状态,以便在 std::transform 中调用 operator() 时,它会添加给定的数字。

在此,

std::transform(v.begin(), v.end(), vi.begin(), lambda_function());

lambda_function() default-creates lambda_function.
的临时实例 lambda_function 是一个 class,不是对象。
lambda_function() 是一个对象。

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance());

您没有传递实例,您正在尝试调用不带参数的 operator()
instance 是一个对象,而不是 class。

要传递实例,写

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance);

std::transform(InIt first, InIt last, OutIt dest, Fn f) 本质上是这样做的:

while (first != last) {
    *dest = f(*first);
    first++;
}

所以当你打电话时

std::transform(v.begin(), v.end(). vi.begin(), lambda_function());

您正在创建类型为 lambda_function 的临时对象并将该对象传递给 transform。在 transform 中,该对象在输入范围的每个元素上被调用,就像在您的代码 int capture = obj(9);.

中一样

要使用您已经创建的对象而不是临时对象,只需将其传入:

lambda_function instance;
std::transform(v.begin(), v.end(), vi.begin(), instance);

注意这段代码在instance之后没有();那将是不带参数调用 operator(),而 lambda_function 没有可以不带参数调用的 operator()

看区别:

lambda_function obj;
int capture = obj(9);
cout << capture << endl;

int capture = lambda_function()(9);
cout << capture << endl;

第二个例子:

std::transform(v.begin(), v.end(). vi.begin(), lambda_function());

对比

lambda_function obj;
std::transform(v.begin(), v.end(). vi.begin(), obj);

最新示例:

std::transform(in.begin(), in.end(), out.begin(), add_x(1));

add_x obj( 1 );
std::transform(in.begin(), in.end(), out.begin(), obj);