8

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

T.C.
  • 133,968
  • 17
  • 288
  • 421
Mat
  • 11,263
  • 10
  • 45
  • 48
  • 1
    std::pair is a struct, the standard says the compiler determines the layout though the order must be maintained, so in the instance of std::pair your compiler may decide to place 3-byte padding after each char for optimal alignment, so no you can't assume contiguous memory layout - end of story. –  Jan 06 '10 at 01:22

3 Answers3

8

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.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 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 Jan 05 '10 at 22:29
  • 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. – C. K. Young Jan 05 '10 at 22:31
  • 1
    @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 Jan 05 '10 at 23:21
  • @Steve: Thanks for explaining! Much clearer than anything I could have written. :-) – C. K. Young Jan 06 '10 at 00:22
  • Mat should also take note that it is OK to do the `memcpy()` if the `myvect` is of type `std::vector`. It might be preferable to purists if `std::copy()` is used instead of `memcpy()`. – Michael Burr Jan 06 '10 at 00:24
  • 1
    @Michael: Only if `struct foo` is a POD type. :-P (Which, for this question, means that `T1` and `T2` also are.) – C. K. Young Jan 06 '10 at 00:36
  • 3
    And that's why std::copy is of interest to more than just purists - it copies non-POD data. – Steve Jessop Jan 06 '10 at 01:04
  • 1
    @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 Jan 06 '10 at 01:10
  • 2
    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` may use 64-bit aligned read/writes without prior checks. – MSalters Jan 06 '10 at 09:36
4

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
  • 273,490
  • 39
  • 460
  • 699
  • `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 Jan 06 '10 at 01:30
  • 1
    `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 Jan 06 '10 at 01:37
0

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
  • 59,826
  • 25
  • 160
  • 281
  • 6
    Actually vector<> IS guaranteed to be a contiguous array. – Fred Larson Jan 05 '10 at 22:25
  • It's pretty much accepted that `std::vector` is contiguous (I think future versions of C++ will specify so), and that `&vec[0]` should be usable as an array of size `vec.size()`. But, doing `memcpy` on non-POD types like `std::pair` is risky, yes. – C. K. Young Jan 05 '10 at 22:27
  • You are forgiven, even in C++03... std::vector is required to use contiguous storage. – Derrick Turk Jan 05 '10 at 22:28
  • @Seva: From the C++03 draft standard 23.2.5, "The elements of a vector are stored contiguously, meaning that if v is a vector where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size()." – Fred Larson Jan 05 '10 at 22:28
  • @Seva: C++03, 23.2.4.1: The elements of a vector are stored contiguously, meaning that if v is a vector where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size(). – Derrick Turk Jan 05 '10 at 22:29
  • 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 Jan 06 '10 at 00:11
  • 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 Jan 06 '10 at 02:26