I suspected as much.
I have seen various people recommending different ways of getting a pointer to
the first element of an std::vector, but they all miss a couple things. The
last I checked, the way memory is allocated and managed in an STL container is
an implementation detail, so there is no guarantee that the contents of a
vector are stored in contiguous memory. Unless the standard has changed since
last I looked to provide such a guarantee, I wouldn't want to make such an
assumption. Further, all bets are off when it comes to member functions that
can change the contents of the vector. One of the frequent issues I see junior
programmers having is that they forget that all iterators are invalidated when
you start adding or removing elements.
Alas, this guarantees some inefficinecies, especially when there is a need to
use multiple libraries. For example, I have written a library that is quite
efficient in use of std::vector in computing various statistics, especially
moving statistics. But it depends on the capabilities of std::vector, and
especially its forward and reverse iterators, for much of its efficiencies. To
use both that code and gsl in the same program, I guess there's little option
but to use gsl's vector view and matrix view on pointers to double arrays (and
at least with those I can guarantee exception safety by having the memory those
arrays use managed by a smart pointer), and bear the cost of one copy of the
input data each time the analysis is required. At least, when doing this for a
time series, and needing a moving window, I can avoid the cost of reallocating
the memory used for the data.
That is not quite correct. The C++ Standard guarantees that vectors are
stored such that the i-th element of a vector v is stored in memory at
the address (&v[0]+i) for all i's. This is the case since C++03. You are
however correct that this was not the case before C++03.
A similar addition to the standard have also been done for the
complex<T> type. Before C++11, the real and imaginary parts of
complex<T> were not garanteed to be contiguous in memory, and I think
there was no guarantee that there were no padding of some sort. C++11
guarantees that. That is, if you have :
complex<double> carray[10];
You are certain that :
((double *)carray)[2*i] => gives the real part of the i-th element
((double *)carray)[2*i+1] => gives the imaginary part of the i-th element.
In practice, it is probable that all implementations of vector and
complex were done this way from the start, but when the C++ standard
comitee realized that these two things were not guaranteed by the
standard, they fixed it (because it would break a lot of codes using
legacy C or Fortran libraries if this was not the case).
Since C++03, you can therefore safely use a "vector<double> v" and
pass "&v[0]" to a C function requiring a "double *", and since C++11,
you can safely cast "complex<T> *" into a "double *" (which will have
twice the length of the initial vector) to a C (or Fortran) function
which expects arrays of T to represent arrays of complex T numbers.
Best,
Maxime Boissonneault