简体   繁体   中英

How do I create a vector of object values from a vector of object pointers?

The "naive" solution is;

std::vector<T> vector_of_objects;
vector_of_objects.reserve(vector_of_pointers.size());
for (T const * p : vector_of_pointers)
    vector_of_objects.push_back(*p);

The above seems cumbersome and perhaps not immediately obvious.

Is there a solution that is at least not significantly less efficient and perhaps a little quicker and more intuitive? I'm thinking C++11 might have a solution that I am not aware of...

Does writing everything in one line mean shorter code? No.

In my opinion these two lines are shorter and more readable:

for (auto p : vector_of_pointers)

    vector_of_objects.emplace_back(*p);

Function std::for_each is not shorter than ranged-based loop, sometime it's bigger due to passing lambda expressions.

Function std::transform is even longer than std::for_each , however the word transform is an advantage to find out what's happening in the following.

You are doing the correct way. Another way is to use built-in algorithms library, like this:

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

int main() {
    // Create a vector of pointers
    std::vector<int*> vptr;
    vptr.push_back(new int(1));
    vptr.push_back(new int(2));

    // Copy to vector of objects
    std::vector<int> vobj;
    std::for_each(vptr.begin(), vptr.end(), [&](int *n) { vobj.emplace_back(*n); });

    // Free the pointers
    std::for_each(vptr.begin(), vptr.end(), [&](int *n) { delete n; });

    // Print out the vector of objects
    std::copy(vobj.begin(), vobj.end(), std::ostream_iterator<int>(std::cout, " "));
    return 0;
}

The idiomatic way would be to use std::transform :

std::transform( vector_of_pointers.begin(),
                vector_of_pointers.end(),
                std::back_inserter( vector_of_objects ),
                []( T* p ) { return *p; } );

Whether this is "better" than what you've written is another question: it has the advantage of being idiomatic, and of actually naming what is going on (which makes the code slightly clearer). On the other hand, the "transformation" is very, very simple, so it would be easily recognized in the loop, and the new form for writing such loops makes things fairly clear as well.

不,你必须在你的解决方案中调用copy-ctor,没有办法解决这个问题。

std::vector<T*> vecPointers;
std::vector<T> vecValues;

for(size_t x=0;x<vecPointers.size();x++)
{
    vecValues.push_back(*vecPointers[x]);
}

I believe that if type T is a custom object then you will need to create a copy constructor for class T.

class T
{
    private:
        int someValue;
    public:
        T()
        {
        }
        T(const T &o)// copy constructor
        {
            someValue = o.someValue;
        }
        virtual ~T()
        {
        }
};

It seems to me that the real question is whether you're doing this often enough for it to be worth writing extra code in one place to clean up the code in the other places.

I can imagine writing a deref_iterator that would allow you to do something like this:

std::vector<T> vector_of_objects{
    deref_iterator(std::begin(vector_of_pointers)), 
    deref_iterator(std::end(vector_of_pointers))};

Now, we're left with the question of whether this is really shorter than the original loop or not. In terms of simple number of key strokes, it's probably going to depend on the names you give things. If you didn't care about readable names, it could be:

vector<T> v_o{d(begin(v_p)), d(end(v_p))};

The short names obviously make it short, but I certainly wouldn't advise them -- if I hadn't just typed this in, I'd have no clue in the world what it meant. A longer name (that needs to be repeated a couple of times) obviously adds more key-strokes, but I can't imagine anybody thinking the readability wasn't worth it.

In any case, the deref_iterator itself would clearly take up some code. An iterator has enough boiler-plate that it typically takes around 100 lines of code or so. Let's (somewhat arbitrarily) decide that this saves one line of code every time you use it. On that basis, you'd have to use it 100 times to break even.

I'm not sure that's accurate in characterizing the code overall -- the code for an iterator is mostly boiler-plate, and other than a typo, there's not much that could really go wrong with it. For the most part, it would be a matter of including the right header, and using it, not of virtually ever having to look at the code for the iterator itself.

That being the case, I might accept it as an improvement even if the total number of lines of code increased. Writing it to use only once would clearly be a loss, but I don't think it'd need to be a full 100 times to qualify as breaking even either.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM