【问题标题】:How to use std::transform and std::mem_fn correctly?如何正确使用 std::transform 和 std::mem_fn?
【发布时间】:2020-04-24 08:07:27
【问题描述】:

我已经实现了一个 C++ 程序,它创建了三个不同的 Person 对象。这些对象被共享并存储在一个向量中。

函数getVector()const std::vector<std::shared_ptr<Person>>& 作为输入并返回std::vector<Person*>。结果向量中的每个元素对应于输入向量中的相应元素。

函数使用std::transformstd::mem_fn

该函数的实现(以及使用std::cout 的相应调用)不起作用。我认为我以错误的方式(错误的论点等)将std::transformstd::mem_fn 一起使用。

我不知道如何使函数getVector() 工作。

到目前为止,这是我的源代码:

ma​​in.cpp

#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>

struct Person
{
    std::string first_name;
    std::string last_name;
    int age;

    Person* getPerson(const std::shared_ptr<Person>& person)
    {
        Person* p = person.get();
        return std::move(p);
    }

    // Overloaded << operator.
    friend std::ostream& operator<<(std::ostream& os, const Person& person)
    {
        os << person.first_name << ", " << person.last_name << ", " << person.age << "\n";
        return os;
    }

    friend std::string to_string(const Person& obj)
    {
        std::ostringstream ss;
        ss << obj;
        return ss.str();
    }
};

std::vector<Person*> getVector(const std::vector<std::shared_ptr<Person>>& people);

int main(void)
{
    Person person01;
    person01.first_name = "Ashley";
    person01.last_name = "Graham";
    person01.age = 23;

    Person person02;
    person02.first_name = "Ada";
    person02.last_name = "Wong";
    person02.age = 45;

    Person person03;
    person03.first_name = "Leon S.";
    person03.last_name = "Kennedy";
    person03.age = 26;

    std::shared_ptr<Person> ps01 = std::make_shared<Person>(person01);
    std::shared_ptr<Person> ps02 = std::make_shared<Person>(person02);
    std::shared_ptr<Person> ps03 = std::make_shared<Person>(person03);

    std::vector<std::shared_ptr<Person>> peoples;

    peoples.emplace_back(std::move(ps01));
    peoples.emplace_back(std::move(ps02));
    peoples.emplace_back(std::move(ps03));

    std::cout << "\nContent of input vector:\n";
    std::cout << "====================================\n";
    for (auto p : peoples)
    {
        std::cout << *p;
    }
    std::cout << "\n";

    std::cout << "Calling function 'getVector()'...\n";

    std::vector<Person*> result = getVector(peoples);

    std::cout << "\nContent of result-vector:\n";
    std::cout << "====================================\n";
    for (auto r : result)
    {
        std::cout << *r;
    }
    std::cout << "\n";

    return 0;
}

// std::transform with std::mem_fn.
std::vector<Person*> getVector(const std::vector<std::shared_ptr<Person>>& people)
{
    Person* person;
    std::shared_ptr<Person> p;
    std::vector<Person*> result;

    std::transform(people.begin(), people.end(), std::back_inserter(result), 
        std::bind1st(std::mem_fn(&Person::getPerson), &p));

    return result;
}

以下是 g++ 编译器的说明:

g++ -std=c++11 -Wall -Wextra main.cpp -o main

【问题讨论】:

  • std::move 在指针上(如在getPerson 中的std::move(p))是没有意义的。只需返回指针即可。
  • @PeteBecker 比没有意义更糟糕,它可能会悲观编译器完成的 RVO。
  • Related。如果您希望bind1st 工作,请将getPerson 的签名更改为采用shared_ptr&lt;Person&gt; 而没有参考。现在你的问题是operator()(const shared_ptr&lt;Person&gt;&amp;)binder1st 中有两个相同的重载。 Working bind1st.

标签: c++ stl shared-ptr stdvector


【解决方案1】:

只需使用 lambda。它使代码更容易理解,您可以摆脱GetPerson 函数。这样做会使您的转换调用看起来像

std::transform(people.begin(), 
               people.end(), 
               std::back_inserter(result), 
               [](const auto& elem){ return elem.get(); });

【讨论】:

  • auto elemconst auto&amp; elem 之间有显着差异吗?
  • @Quest 在这种情况下,是的。仅使用auto 会创建一个副本,这是不必要的,因为shared_ptr 的原子引用计数非常昂贵。通过参考,我们降低了所有成本。如果它类似于int,只要我不需要更新它的值,我就会按值取值。
  • "auto elem 和 const auto& elem 之间有显着区别吗?" - 是的。一个复制(复制一个可能很昂贵的对象),另一个不复制。
  • @NathanOliver 非常感谢您的回复。源代码只是一个最小的例子,所以不相关的部分被删除了。我已经以两种方式实现了getVector() 函数:lambda 表达式(工作)和std::transformstd::mem_fn(不工作)。我想知道如何使用std::transformstd::mem_fn 实现该功能,以便比较这两个功能(可读性和编程工作量)。
  • @RainerH。 mem_fn 确实没有一个好的方法来做到这一点。由于GetPerson 是一个非静态成员函数,您需要有一个Person 实例来调用该函数。您必须将Person 绑定到mem_fn 的返回,并为转换将传递给它的共享指针添加placeholder。这看起来像 Person p; std::transform(..., std::bind(std::mem_fn(&amp;Person::getPerson), p, _1)); 但此时您可以直接使用 bind 。 FWIW,只需使用 lambda。这是标准对流,大多数人都能理解。
猜你喜欢
  • 1970-01-01
  • 2012-07-25
  • 1970-01-01
  • 2015-07-18
  • 2016-08-03
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
相关资源
最近更新 更多