views:

529

answers:

3

Hi!

is it safe to memcopy myvect.size()*sizeof(foo) bytes from the memoryadress of the first element of a

std::vector<std::pair<T1, T2> > myvect

into an array of

struct foo{
    T1 first;
    T2 second;
}

if the array is allocated with the same number of elements as the vector's size?

thanks

+5  A: 

No, a class containing T1 and T2 is not guaranteed the same layout or alignment as std::pair<T1, T2>, at least in C++98 (since std::pair is not a POD type). The story may be different in C++0x.

Chris Jester-Young
but std::pair is just no POD because it has a user defined constructor, no? and this shouldn't change something on the memory layout - or does it?
Mat
In C++98, implementations are allowed to use a different layout for non-POD types versus POD types. In C++0x, if I remember right, there is a special designation for types that have constructors, but have no base classes, virtual functions, or nontrivial members. I can't remember its name, but the idea is that such types are simple enough to be `memcpy`able.
Chris Jester-Young
@Chris: standard-layout, but a standard-layout class isn't *necessarily* safe for copying, it just means (in effect) that the compiler hasn't inserted any nasty surprises. A RAII class containing a pointer to a heap-allocated object, that it frees on destruction and clones on assignment, is standard-layout but not POD, and probably shouldn't be copied with memcpy.
Steve Jessop
@Steve: Thanks for explaining! Much clearer than anything I could have written. :-)
Chris Jester-Young
Mat should also take note that it is OK to do the `memcpy()` if the `myvect` is of type `std::vector<struct foo>`. It might be preferable to purists if `std::copy()` is used instead of `memcpy()`.
Michael Burr
@Michael: Only if `struct foo` is a POD type. :-P (Which, for this question, means that `T1` and `T2` also are.)
Chris Jester-Young
And that's why std::copy is of interest to more than just purists - it copies non-POD data.
Steve Jessop
@Chris: right - I overlooked that detail (rather, I assumed they were POD types - a dangerous assumption). However, `std::copy()` would work even if they aren't POD (as long as the type supports assignment). So that makes my 'purist' crack wrong - it should simply be used. Hopefully, optimizations would cause the compiler to spit out `memcpy()` equivalent code if the the types are POD - I'll have to take a look at some asm output to see if this really happens in practice. In any case, the output would likely be good enough except when it isn't (how's that for hand waving?).
Michael Burr
Actually, some compilers create code for `std::copy()` that is _better_ than `std::memcpy()` - it's easy for specializations of `std::copy()` to benefit from type alignment restrictions. E.g. `std::copy<double*>` may use 64-bit aligned read/writes without prior checks.
MSalters
A: 

In general, no. On some platforms/compilers/STL implementations it might be, but don't do it anyway. You'd be relying on the implementation details of both pair<> and vector<>.

I myself have committed the sin of relying on vector<> being a contiguous array. For that, I deeply repent. But the pair<>... Just say no.

Seva Alekseyev
Actually vector<> IS guaranteed to be a contiguous array.
Fred Larson
@Fred: cite please?
Seva Alekseyev
Chris Jester-Young
You are forgiven, even in C++03... std::vector is required to use contiguous storage.
Derrick Turk
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2001/n1292.html item 69.
Chris Jester-Young
Fred Larson
Derrick Turk
@Fred: foiled again! haha.
Derrick Turk
Some other links for more info on contiguous `std::vector<>`: http://stackoverflow.com/questions/247738/is-it-safe-to-assume-that-stl-vector-storage-is-always-contiguous and http://herbsutter.wordpress.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/
Michael Burr
Thanks all. I don't exactly recall the timing of the sin in question - maybe it was before year 03, maybe after. Regardless, I felt uneasy at the time.
Seva Alekseyev
+2  A: 

The answer to the question you didn't ask is probably std::transform:

struct pairToFoo {
    // optionally this can be a function template.
    // template<typename T1, typename T2>
    foo operator()(const std::pair<T1,T2> &p) const {
        foo f = {p.first, p.second};
        return f;
    }
};

std::transform(myvect.begin(), myvect.end(), myarray, pairToFoo());

Or std::copy, but give foo an operator= taking a pair as parameter. This assumes you can re-write foo, though:

struct foo {
    T1 first;
    T2 second;
    foo &operator=(const std::pair<T1,T2> &p) {
        first = p.first;
        second = p.second;
        return *this;
    }
};

std::copy(myvect.begin(), myvect.end(), myarray);
Steve Jessop
`std::transform()` is something I never remember. Ever. Maybe now that I've said that publicly, it'll pop into my head sometimes.
Michael Burr
`std::transform` is what you get instead of map and zipWith. So maybe if every time you forget about transform, you re-write the relevant function in Haskell, then you'll remember it. If only to avoid having to write Haskell.
Steve Jessop