Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Looping through objects and pointers with the same code

Tags:

c++

pointers

stl

So I have two containers

set<Person>
vector<Person*>

Is there any way for the following transform lambda code to be modified such that either container can be used?

transform(container.begin(), container.end(), back_inserter(data),
          [](const Person* p) { return PairResults(p->getTime(), p->getName()); }
);

Right now I'm only able to get it to work on the vector.

Thanks.

>> Solution Code Here

Many thanks to Andrew for his help on this one!

like image 536
David Avatar asked Aug 02 '14 09:08

David


2 Answers

You can do it using a little trick with function overload: define two functions that produce a pointer to Person, like this:

const Person* make_ptr(const Person* p) { return p; }
const Person* make_ptr(const Person& p) { return &p; }

The next problem is unifying the type of the element for the lambda: unfortunately, it is not legal to say [](auto p) {...} and let the compiler pick Person or Person* for you. Using a template solves this problem:

template<typename T, typename R>
void xform(vector<T>& from, vector<R>& to) {
    transform(from.begin(), from.end(), back_inserter(to),
        [&](T &p) { return PairResults(make_ptr(p)->getTime(), make_ptr(p)->getName()); }
    );
}

Now everything works - you can call xform with a container of Person or Person*, and get the correct results.

Demo on ideone.

like image 82
Sergey Kalinichenko Avatar answered Sep 29 '22 23:09

Sergey Kalinichenko


You could use a proxy object that accepts pointer to Person or ref to Person and gives them both pointer semantics:

Live example: http://ideone.com/Wk2VMx

#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
#include <string>

struct Person {
    int age;
    std::string name;
};

class PersonProxy {
    public:
        PersonProxy(Person& p)
        : person(&p) {}
        PersonProxy(Person* p)
        : person(p) {}

        Person& operator*() { return *person; }
        Person* operator->() { return person; }

    private:
        Person* person;
};

int main()
{
    std::vector<Person> p1;
    p1.push_back(Person{42, "Bob"});

    Person bill{30, "Bill"};
    std::set<Person*> p2;
    p2.insert(&bill);

    std::vector<int> data;

    auto the_lambda = [](PersonProxy pp) {return pp->age;};

    std::transform(p1.begin(), p1.end(), std::back_inserter(data), the_lambda);
    std::transform(p2.begin(), p2.end(), std::back_inserter(data), the_lambda);

    for (int age : data)
    {
        std::cout << age << "\n";
    }

}

Another approach would be to use a functor instead of the lambda, and define operator()(Person&) and operator()(Person*), but the approach above is to provide a type with which you can write the lambdas, and for which implicit conversions from Person and Person* are available.

like image 22
Andrew Avatar answered Sep 30 '22 00:09

Andrew