通过 std::transform 避免复制构造

Avoid copy construction by std::transform

我正在调用 std::transform 时使用一个 lambda,它通过引用获取并返回对向量元素的引用。但是,根据我的程序输出,调用了复制构造函数并且对象不一样。

代码:

#include <algorithm>
#include <iostream>
#include <vector>

class Math
{
private:
    int val_ = 5;
public:
    Math(const Math& m) {
        std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
    }
    Math(int val) : val_(val) {
        std::cout << "Object constructed with " << val << std::endl;
    }
};

int main()
{
    std::vector<Math> v_math = { { 5 }, { 10 } };
    std::transform(
        begin(v_math),
        end(v_math),
        begin(v_math), 
        [](const Math& m)-> const Math&  {
            return m;
        });
}

输出(神马):

Object constructed with 5
Object constructed with 10
Copy constructor, our address: 0x23d7ec0, his address: 0x7fff9dc499a8
Copy constructor, our address: 0x23d7ec4, his address: 0x7fff9dc499ac

所以我现在不清楚三件事:

  1. 为什么对象不同?他们不应该是一样的吗?
  2. 为什么一个对象的地址比另一个大?这是因为复制到的对象保留在具有偏移指针的堆栈上吗?
  3. 我怎样才能避免复制构造(实际上我只是“误用”std::transform 作为在每个 std::vector 元素上调用 lambda 的声明方式)?

副本与您对 std::transform 的使用无关。它们在您构造 v_math std::vector 时发生,因为您使用的是 std::initializer_list 构造函数,它在构造期间强制复制。

在您的 std::transform 调用中,调用了 operator=(const Math&),请将您的代码更改为以下内容以查看此内容。

class Math
{
private:
    int val_ = 5;
public:
    Math(const Math& m) {
        std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
    }
    Math(int val) : val_(val) {
        std::cout << "Object constructed with " << val << std::endl;
    }

    Math& operator=(const Math& other) {
        val_ = other.val_;
        std::cout << "Operator=(const Math&) called!\n";
        return *this;
    }
};

int main()
{
    std::vector<Math> v_math = { { 5 }, { 10 } };

    std::cout << "After constructing v_math!\n";

    std::transform(
        begin(v_math),
        end(v_math),
        begin(v_math),
        [](const Math& m)-> const Math& {
            return m;
        });
    std::cout << "After std::transform call!\n";
}