4

if I have an array such as:

struct S {... };

S m_aArr[256];

and I want to use this to construct a vector such as:

std::vector<S*> m_vecS;

Is there anyway to do this rather than looping through and pushing back &m_aArr[i] ? I understand that I cannot use the conventional method of using std::begin and std::end on the array since the vector is one of pointers and the original array is one of objects, and so we cannot just pass in a block of memory.

hmjd
  • 120,187
  • 20
  • 207
  • 252
makar
  • 497
  • 1
  • 4
  • 16

3 Answers3

12

You could use the standard library to do the iteration and pushing back for you:

std::transform(std::begin(m_aArr), std::end(m_aArr),
               std::back_inserter(m_vecS), std::addressof<S>);

This will transform each of the elements in m_aArr by applying the std::addressof<S> function to them. Each of the transformed elements is then push_backed into m_vecS by the std::back_inserter iterator.

To do this prior to C++11, you won't have access to std::begin, std::end, or std::addressof, so it'll look more like this:

std::transform(m_aArr, m_aArr + 256, std::back_inserter(m_vecS), boost::addressof<S>);

This uses boost::addressof.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • 1
    +1 - I was going to suggest a similar thing but with a lambda - I didn't even know `std::addressof` existed. – Yuushi Apr 24 '13 at 12:17
  • @R.MartinhoFernandes Of course, not sure why I didn't do that in the first place. Thanks. – Joseph Mansfield Apr 24 '13 at 12:18
  • @Yuushi `std::addressof` works even if `operator&` is overloaded for `S` - so it's superior in this case. – jrok Apr 24 '13 at 12:19
  • Thank you, a very elegant solution – makar Apr 24 '13 at 12:30
  • it would be nice to have a `std::valueof` that does the reverse so that you could also push the results back into some other vector (e.g. after sorting a vector of pointers to big data) – TemplateRex Apr 24 '13 at 12:52
  • Does this answer the question? The OP wants to **construct** a `vector` with certain properties. Not take an already constructed vector and insert those elements. – Walter Apr 24 '13 at 13:15
  • @Walter The initial vector is empty. The transformed elements are put in the vector by `push_back`. – Joseph Mansfield Apr 24 '13 at 13:16
  • 1
    @Walter I think they are using the term "construct" loosely. They just want to set up the vector in one way or another to have these specific pointers in it. – Joseph Mansfield Apr 24 '13 at 15:18
  • Even before C++11, we can easily learn the size of an array. `template size_t array_length(const T (&)[N]) { return N; }` This would be useful instead of the explicit `256` in the current answer. – Aaron McDaid Oct 17 '13 at 17:33
5

You could let std::transform perform the loop:

transform(std::begin(a), std::end(a), std::back_inserter(v), 
          [] (S& s) { return &s; });

Notice, that you do not need to fully qualify the name std::transform, because the function name will by found by ADL.

This is a complete program to test its behavior:

#include <iostream>
#include <vector>
#include <algorithm> // <== Required for std::transform
#include <iterator>  // <== Required for std::back_inserter, std::begin, std::end

struct S
{
    S() : i(0) { }
    S(int i_) : i(i_) { }
    int i;
};

int main()
{
    S a[256] = { 42 }; // Copy-initializes first element from 42,
                       // default-constructs all other elements

    std::vector<S*> v;
    transform(std::begin(a), std::end(a), std::back_inserter(v), 
              [] (S& s) { return &s; });

    std::cout << v.size() << std::endl; // Prints 256
    std::cout << v[0]->i << std::endl; // Prints 42
    std::cout << v[1]->i << std::endl; // Prints 0
}

And here is a live example.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Cheers, this is a good idea. I am beginning to realise the power of std::transform – makar Apr 24 '13 at 12:31
  • why are you typing out `std::` prefixes all over this sample, except for `transform` where you rely on ADL? E.g. what if the call to `transform` happened in some generic piece of code where you could not be sure to have arguments that live in namespace `std`? – TemplateRex Apr 24 '13 at 12:58
  • @rhalbersma: Well, that's because in the other places it is necessary. It would not be if I used some `using` declarations or `using` directives, and I could - there is no particular reason why I am not using them here, this is just a demonstration. Also, it is enough that *one* argument lives in the `std` namespace for `ADL` to lookup `transform` in `std`, and the result of `std::back_inserter` does live in `std`, no matter where the call is performed. – Andy Prowl Apr 24 '13 at 13:02
  • I understand your argument, but why bring ADL into play when you don't have to rely on it? Some refactoring later and there is not longer a back inserter etc. Typing `std::` takes 5 keystrokes, and it will always get it right (unless you actually want to also be able to call user-defined versions of `transform`). – TemplateRex Apr 24 '13 at 13:09
  • @rhalbersma: Well, I see your point. I sometimes prefer to spare those 5 characters, but yes, not the best example here – Andy Prowl Apr 24 '13 at 13:18
1

A solution using std::generate_n() that performs the single allocation of the std::vector instead of potentially multiple via std::vector::push_back():

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

int main()
{
    struct S {};
    S a[128];
    S* ap = a;

    std::vector<S*> v(sizeof(a)/sizeof(a[0]));
    std::generate_n(std::begin(v), v.size(), [&]() { return ap++; });

    for (size_t i = 0; i < v.size(); i++)
    {
        if (&a[i] != v[i]) // Ensure same address at each element.
        {
            std::cerr << "Error\n";
            break;
        }
    }
    return 0;
}

See online at http://ideone.com/73nKST .

hmjd
  • 120,187
  • 20
  • 207
  • 252