I've seen code that uses a const std::vector, however can't see why it wouldn't make more sense to simply use an std::array instead.
The values for the vector seem to be initialized at compile-time.
What is the benefit of a const std::vector?
Edit: The vector was not big, only a couple of strings, however I see that this may be one advantage.
const std::vector lets you have a "fixed sized array" whose size you only know at run time but still allows you to have all the benefits of a standard container. If you used a raw/smart pointer in your code you will need to manually pass the size of the array that it points to into the function(s) that need to know the size of the array.
The std::vector could be used with a large list of data, because it could use Dynamic Allocated Array to store values and before C++20 it's doesn't have a constexpr constructor. But std::array uses a raw C-Array and it could be used at compile time with the stack size restricted list of data. So:
std::array const is good for:
Where your data size is less than the stack size.
Where data locality is important.
You want your list at the compile time (before C++20).
std::vector const is good for:
Where your data size is large than the stack size.
It's quite common to see C++ code that expects references to vectors, sometimes const, something like this:
auto do_sum(std::vector<int> const& numbers) -> int {
return std::accumulate(numbers.begin(), numbers.end(), 0);
}
Even though the good intentions are not about not copying memory, this code require the caller to allocate a std::vector, even though the amount of value and the actual values might be known at compile time.
This might be fixed by using a span instead:
auto do_sum(std::span<int const> numbers) -> int {
return std::accumulate(numbers.begin(), numbers.end(), 0);
}
Now this code don't require the users to allocate a vector for the array, and might use std::array, or plain C arrays.
Also, sometimes you don't know what are the numbers of element the array can have, and might be determined at runtime. Consider this:
auto create_vector() -> std::vector<int> {
auto vec = std::vector<int>{};
if (/* runtime condition */) {
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
} else {
vec.push_back(4);
vec.push_back(5);
}
return vec;
}
int main() {
std::vector<int> const vec = create_vector();
}
As you can see here, as the vector is moved from scope to scope, the const-ness changes and is expressing the intent of the developer to only make it mutable in some initializing scopes.
Because the std::array needs you to specify the size as part of the type. By using std::vector the compiler can work that out for your dynamically.
This matters in maintenance situations as it prevents errors. You only add/remove a string from the initializer of the object and the vector will have the correct size automatically. If you use an array you need to add/remove the string and change the type of the array.
const std::vector<std::string> dataV = { "A", "B", "C" };
const std::array<std::string, 3> dataA = { "A", "B", "C" };
If I now modify these to only have two values.
// The vector will auto resize
const std::vector<std::string> dataV = { "A", "B"};
// This will still be of size three.
// This is not usually what you want.
const std::array<std::string, 3> dataA = { "A", "B"};
// The person modifying the code has to manually spot that and
// change the type to explicitly have two member array. Note
// It may not be as obvious as you think as the type may
// be hidden with a type alias of some description
using DataStore = std::array<std::string, 3>;
/// Lots of code:
DataStore dataA = { "A", "B"}; // Would you have spotted.
Related
There are many design issues I have found with this, particularly with passing std::array<> to functions. Basically, when you initialize std::array, it takes in two template parameters, <class T and size_t size>. However, when you create a function that requires and std::array, we do not know the size, so we need to create template parameters for the functions also.
template <size_t params_size> auto func(std::array<int, params_size> arr);
Why couldn't std::array take in the size at the constructor instead? (i.e.):
auto array = std::array<int>(10);
Then the functions would look less aggressive and would not require template params, as such:
auto func (std::array<int> arr);
I just want to know the design choice for std::array, and why it was designed this way.
This isn't a question due to a bug, but rather a question why std::array<> was designed in such a manner.
std::array<T,N> var is intended as a better replacement for C-style arrays T var[N].
The memory space for this object is created locally, ie on the stack for local variables or inside the struct itself when defined as a member.
std::vector<T> in contrary always allocate it's element's memory in the heap.
Therefore as std::array is allocated locally, it cannot have a variable size since that space needs to be reserved at compile time. std::vector in the other hand has the ability to reallocate and resize since its memory is unbounded.
As a consequence, the big advantage of std::array in terms of performance is that it eliminates that one level of indirection that std::vector pays for its flexibility.
For example:
#include <cstdint>
#include <iostream>
#include <vector>
#include <array>
int main() {
int a;
char b[10];
std::vector<char> c(10);
std::array<char,10> d;
struct E {
std::array<char,10> e1;
std::vector<char> e2{10};
};
E e;
printf( "Stack address: %p\n", __builtin_frame_address(0));
printf( "Address of a: %p\n", &a );
printf( "Address of b: %p\n", b );
printf( "Address of b[0]: %p\n", &b[0] );
printf( "Address of c: %p\n", &c );
printf( "Address of c[0]: %p\n", &c[0] );
printf( "Address of d: %p\n", &d );
printf( "Address of d[0]: %p\n", &d[0] );
printf( "Address of e: %p\n", &e );
printf( "Address of e1: %p\n", &e.e1 );
printf( "Address of e1[0]:%p\n", &e.e1[0] );
printf( "Address of e2: %p\n", &e.e2);
printf( "Address of e2[0]:%p\n", &e.e2[0] );
}
Produces
Program stdout
Stack address: 0x7fffeb115ed0
Address of a: 0x7fffeb115eb0
Address of b: 0x7fffeb115ea6
Address of b[0]: 0x7fffeb115ea6
Address of c: 0x7fffeb115e80
Address of c[0]: 0x1cad2b0
Address of d: 0x7fffeb115e76
Address of d[0]: 0x7fffeb115e76
Address of e: 0x7fffeb115e40
Address of e1: 0x7fffeb115e40
Address of e1[0]:0x7fffeb115e40
Address of e2: 0x7fffeb115e50
Address of e2[0]:0x1cad2d0
Godbolt: https://godbolt.org/z/75s47T56f
Not an answer, really, because I used to despise std::array<> for the same reasons as you — anything with Monadic qualities are not good design (IMNSHO).
Fortunately, C++20 has the solution: a dynamic std::span<>.
#include <array>
#include <iostream>
#include <span>
namespace detail
{
void print( const std::span<const int> & xs )
{
for (size_t n = 0; n < xs.size(); n++)
std::cout << xs[n] << " ";
}
}
void print( const std::span<const int> & xs )
{
std::cout << "{ ";
detail::print( xs );
std::cout << "}\n";
}
void add( const std::span<int> & xs, int n )
{
for (int & x : xs)
x += n;
}
int main()
{
std::array<int,5> xs { 1, 2, 4, 6, 10 };
add( xs, 1 );
print( xs );
}
Notice that the span itself is const in all cases, but the elements themselves are modifiable unless they too are tagged const. This is exactly what an array is like.
std::span is a C++20 object. I know that MS and maybe others had a array_view in older versions of their libraries.
tl;dr
Use std::array only to declare your array object. Pass it around with a dynamic std::span.
std::array vs C array
The use-case for std::array is actually very narrow: encapsulate a fixed-size array as a first-class container object (one that can be copied, not just referenced).
At first blush this doesn’t seem to be much of an improvement over standard C-style arrays:
typedef int myarray[10]; // (1)
using myarray = std::array<int,10>; // (2)
void f( myarray a );
But it is! The difference is in what f() actually gets:
For a C-style array, the argument is just a pointer — a reference to the caller’s data (that you can modify!). You know the size of the referenced array (10), but writing code to get that size is not straight-forward even with the usual C array-size idiom (sizeof(myarray)/sizeof(a[0]), since sizeof(a) is the size of a pointer).
For the std::array, the argument value is an actual local copy of the caller’s data. If you want to be able to modify the caller’s data then you need to be explicit about declaring the formal argument as a reference type (myarray & a) or just to avoid an expensive copy (const myarray & a). This falls in line with how other C++ objects are passed. And though the size is still 10, your code can query the size of the array with the usual C++ container idiom: a.size()!
The usual way C overcomes this is to clutter the call site and formal argument lists with information about the array size so that it doesn’t get lost.
int f( int array[], size_t n ) // traditional C
{
printf( "There are %zu elements.\n", n );
recurse with f( array, n );
}
int main(void)
{
int my_array[10];
f( my_array, ARRAY_SIZE(my_array) );
The std::array way is cleaner.
int f( std::array<int,10> & array ) // C++
{
std::cout << "There are " << array.size() << " elements.\n";
recurse with f( array );
}
int main()
{
std::array<int,10> my_array;
f( my_array );
But while cleaner, it is significantly less flexible than the C array, simply because its length is fixed. A caller cannot pass a std::array<int,12> to the function, for example.
I’ll refer you to the other good answers here to consider more about container choice when handling arrayed data.
If you have a problem with std::array and you think std::span is a solution, now you will have two problems.
More seriously, without knowing what kind of conceptual operation is func it is difficult to tell what is the right alternative.
First, if you want or can exploit to know the size at compile-time there is nothing cooler than what you are trying to avoid.
template<std::size_t N>
void func(std::array<int, N> arr); // add & or && or const& if appropiate
Imagine it, knowing the size at compile time can allow you and the compiler to do all sorts of tricks, like unrolling loops completely or verifying logic at compile time (e.g. if you know the size must be smaller or bigger than a constant).
Or the coolest trick of all, not needing to allocate memory for any auxiliary operation inside func (because you know the size of the problem a priori).
If you want a dynamic array, use (and pass) a std::vector.
void func(std::vector<int> dynarr); // add & or && or const& if appropiate
But then you force your caller to use std::vector as the container.
If you want a fixed array, and it will work with everything,
template<class FixedArray>
void func(FixedArray dynarr); // add & or && or const& if appropiate
Ask yourself, how specific is your function such that you really really want to make it work with any size of std::array but not with std::vector?
Why specifically ints even?
template<class ArithmeticRange>
void func(ArithmeticRange dynarr); // add & or && or const& if appropiate
There are a few contiguous containers and ranges in C++ std. They serve different purposes. There are also a few techniques for passing them around.
I'll try to be exhaustive.
std::array<int, 7>
this is a buffer of 7 ints. They are stored within the object itself. Putting an array somewhere is putting enough storage for exactly 7 ints in that location (plus possible padding for alignment reasons, but that is at the end of the buffer).
You use this when, at compile time, you know exactly how big something is, or need to know.
std::vector<int>
this object holds ownership of a buffer of ints. The memory that holds those ints is dynamically allocated and can change at runtime. The object itself is usually 3 pointers in size. It has some strategies to grow that avoids doing N^2 work when you keep adding 1 element at a time to it.
This object can be efficiently moved -- it will steal the buffer if the old object is marked (by std::move or other ways) as being safe to steal state from.
std::span<int>
This represents an externally owned sequence of ints, possibly stored in a std::array or owned by a std::vector, or stored somewhere else. It knows where in memory it starts and when it ends.
Unlike the two above, it is not a container, but a range or a view of the contents. So you can't assign spans to each other (the semantics are confusing), and you are responsible to ensure that the source buffer lasts "long enough" that you don't use it after it is gone.
span is often used as a function argument. In your case, it probably solves most of your problem -- it lets you pass arrays of different sizes to a function, and within that function you can read or write the values.
span followed pointer semantics. That means const std::span<int> is like a int*const -- the pointer is const, but the thing pointed to is not! You are free to modify the elements in const std::span<int>. In comparison, std::span<const int> is like a int const* -- the pointer is not const, but the thing pointed to is. You are free to change what range of elements the span refers to in std::span<const int>, but you aren't allowed to modify the elements themselves.
A final technique is auto or templates. Here we implement the body of the function in the header (or equivalent) and leave the type unconstrained (or, constrained by concepts).
template<std::size_t N>
int total0( std::array<int, N> const& elems ) {
int r = 0;
for (int e:elems) r+=e;
return r;
}
int total1( std::vector<int> const& elems ) {
int r = 0;
for (int e:elems) r+=e;
return r;
}
int total2( std::span<int const> elems ) {
int r = 0;
for (int e:elems) r+=e;
return r;
}
int total3( auto const& elems ) {
int r = 0;
for (int e:elems) r+=e;
return r;
}
template<class Ints>
int total4( Ints const& elems ) {
int r = 0;
for (int e:elems) r+=e;
return r;
}
notice these all have the same implementation.
total3 and total4 are identical; you need a more modern compiler to use total3 syntax.
total1 and total2 allow you to split the implementation into a cpp file away from the header file. Also, code generation isn't done for different arguments.
total0, total3 and total4 all result in different code to be generated based on the type of the arguments. This can cause binary bloat issues, especially if the body was more complex than shown, and causes build time problems in larger projects.
total1 won't work with a std::array directly. You can do total1({arr.begin(), arr.end()}) which would copy the contents to a dynamic vector before using the code.
Finally, note that span<int> is the closest you get to the C way of arr[], size. Span is, in essence, a pointer-to-first and length pair, with utility code wrapping it.
The main purpose of a C++11 std::array<> is to be a decent replacement for C-style arrays [], especially when they're declared with new and dismissed with delete[].
The main goal here is to get an official, managed object that serves as an array, while maintaining as constant expressions everything that can be.
Principal issues with regular arrays is that since they're not actually objects, one cannot derivate a class from them (forcing you to implement iterators) and are a pain when you copy classes that uses them as object properties.
Since new, delete and delete[] return pointers, you need each time either to implement a copy constructor that will declare another array them copy its content or maintaining your own dynamic reference counter on it.
From this perpective, std::array<> is a good way to declare purely static arrays that will be managed by the language itself.
I have an array char* source and a vector std::vector<char> target. I'd like to make the vector target point to source in O(1), without copying the data.
Something along these lines:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
Why is it not possible to manually change where a vector points to? Is there some specific, unmanageable problem that would arise if this was possible?
C++ vector class supports adding and deleting elements, with guaranteed consecutive order in memory. If you could initialize your vector with existing memory buffer, and add enough elements to it, it would either overflow or require reallocation.
The interface of vector assumes that it manages its internal buffer, that is, it can allocate, deallocate, resize it whenever it wants (within spec, of course). If you need something that is not allowed to manage its buffer, you cannot use vector - use a different data structure or write one yourself.
You can create a vector object by copying your data (using a constructor with two pointers or assign), but this is obviously not what you want.
Alternatively, you can use string_view, which looks almost or maybe exactly what you need.
std::vector is considered to be the owner of the underlying buffer. You can change the buffer but this change causes allocation i.e. making a copy of the source buffer which you don't want (as stated in the question).
You could do the following:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
but again std::vector::assign:
Replaces the contents with copies of those in the range [first, last).
So copy is performed again. You can't get away from it while using std::vector.
If you don't want to copy data, then you should use std::span from C++20 (or create your own span) or use std::string_view (which looks suitable for you since you have an array of chars).
1st option: Using std::string_view
Since you are limited to C++17, std::string_view might be perfect for you. It constructs a view of the first 3 characters of the character array starting with the element pointed by source.
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
2nd option: Using std::span from C++20
std::span comes from C++20 so it might not be the most perfect way for you, but you might be interested in what it is and how it works. You can think of std::span as a bit generalized version of std::string_view because it is a contiguous sequence of objects of any type, not just characters. The usage is similar as with the std::string_view:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
3rd option: Your own span
If you are limited to C++17, you can think of creating your own span struct. It might still be an overkill but let me show you (btw take a look at this more elaborated answer):
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
So the usage is the same as with the C++20's version of std::span.
The std::string_view and std::span are good things to have (if you have compiler version supporting them). Rolling your own similars is ok too.
But some people miss the whole point why one would want to do this exactly to a vector:
Because you have an API that gives Struct[] + size_t and give you ownership
and you also have an API that accepts std::vector<Struct>
ownership could be easily transferred into the vector and no copies made!
You can say: But what about custom allocators, memory mapped file pointers, rom memory that I could then set as the pointer?
If you are already about to set vector internals you should know what you are doing.
You can try to supply a "correct" allocator in those cases to your vector actually.
Maybe give a warning on compiling this code yes, but it would be nice if it would be possible.
I would do it this way:
std::vector would get a constructor that asks for a std::vector::raw_source
std::vector::raw_source is an uint8_t*, size_t, bool struct (for now)
bool takeOwnership: tells if we are taking ownership (false => copy)
size_t size: the size of the raw data
uint8_t* ptr: the pointer to raw data
When taking ownership, vector resize and such uses the vectors allocation strategy as you otherwise provided with your template params anyways - nothing new here. If that does not fit the data you are doing wrong stuff!
Yes API I say look more complicated than a single "set_data(..)" or "own_memory(...)" function, but tried to make it clear that anyone who ever uses this api pretty much understands implications of it automagically. I would like this API to exists, but maybe I still overlook other causes of issues?
I am following this example to make an adjacency list. However it seems like the vector size cannot be dynamic.
Visual studio throws an error
expression did not evaluate to a constant
on this line
vector<int> adj[V];
The strange thing is that the same exact code works correctly on codeblocks IDE.
I've tried replacing the above line with vector<int> adj; but then I cannot send the vector as a parameter to addEdge(adj, 0, 1); as it throws another error about pointers which I also don't know how to correct.
What could I do to dynamically create my vector?
C++ - How to create a dynamic vector
You don't need to do that for this example. But if you did need it, you could use std::make_unique.
The linked example program is ill-formed. I recommend to not try to learn from that. The issue that you encountered is that they use a non-const size for an array. But the size of an array must be compile time constant in C++. Simple fix is to declare the variable type as const:
const int V = 5;
I've tried replacing the above line with vector<int> adj;
You can't just replace an array of vectors with a single vector and expect the program to work without making other changes.
I need the size to be dynamic as it will only be known at compile time.
Assuming you meant to say that the size will only be known at runtime, the solution is to use a vector of vectors.
As written by eerorika, the example code isn't a good one, and you should avoid using raw arrays like that. An array in C/C++ is of static size, each vector in this array is dynamic, but the entire array is not!
There are two approaches for such a question. Either use adjacency lists (which is more common):
#include <vector>
#include <stdint.h>
class Vertix
{
public:
Vertix(uint64_t id_) : id(id_) {}
uint64_t get_id() const { return id; }
void add_adj_vertix(uint64_t id) { adj_vertices.push_back(id); }
const std::vector<uint64_t>& get_adj_vertices() const { return adj_vertices; }
private:
uint64_t id;
std::vector<uint64_t> adj_vertices;
};
class Graph
{
public:
void add_vertix(uint64_t id)
{
vertices[id] = Vertix(id);
}
void add_edge(uint64_t v_id, uint64_t u_id)
{
edges.emplace_back(u_id, v_id);
vertices[u_id].add_adj_vertix(v_id);
}
private:
std::vector<Vertix> vertices;
std::vector<std::pair<uint64_t, uint64_t>> edges;
};
or use double vector to represent the edges matrix:
std::vector<std::vector<uint64_t>> edges;
But it isn't a real matrix, and you cannot check if (u, v) is in the graph in O(1), which misses the point of having adjacency matrix. Assuming you know the size of Graph on compile time, you should write something like:
#include <array>
#include <stdint.h>
template <size_t V>
using AdjacencyMatrix = std::array<std::array<bool, V>, V>;
template <size_t V>
void add_edge(AdjacencyMatrix<V>& adj_matrix, uint64_t u, uint64_t v)
{
if (u < V && v < V)
{
adj_matrix[u][v] = true;
}
else
{
// error handling
}
}
Then you can use AdjacencyMatrix<5> instead of what they were using on that example, in O(1) time, and although it has static size, it does work as intended.
There’s no need to use C-style arrays in modern C++. Their equivalent is std::array, taking the size as a template parameter. Obviously that size can’t be a runtime variable: template parameters can be types or constant expressions. The compiler error reflects this: std::array is a zero cost wrapper over an internal, raw “C” array.
If the array is always small, you may wish to use a fixed-maximum-size array, such as provided by boost. You get all performance benefits of fixed size arrays and can still store down to zero items in it.
There are other solutions:
If all vectors have the same size, make a wrapper that takes two indices, and uses N*i1+i2 as the index to an underlying std::vector.
If the vectors have different sizes, use a vector of vectors: std::vector>. If there are lots of vectors and you often add and remove them, you may look into using a std::list of vectors.
all
I've a legacy code which in draft does something like this:
// sadly I have to use this structure
struct LegacyStruct {
int* values;
}
LegacyStruct* LgStr;
....
std::vector<int> vec;
// fill vector in some way here
size_t sz = vec.size();
LgStr->values = new int[sz];
std::copy(vec.begin(), vec.end(), &LgStr->values[0]);
vec can be huge and I need to avoid copying it to int*.
Is there a way to do it?
I tried following:
// type of new operator explained in More Effective C++
LgStr->values = new (&vec[0])int[vec.size()];
Ok, values points to the beginning of vec inner array, but it destroyed when vec is out of scope. But I have to keep it..
&vec[0] = nullptr; // does not compile of course
So question is: is it possible to apply move semantics in this case?
Or maybe some other trick?
The short answer is that no, there isn't any way to transfer ownership of a vector's buffer outside the vector.
I think your best option is to make sure that the vector just doesn't die by using a wrapper:
class LegacyStructWrapper : private boost::noncopyable // Or declare private copy constructor/copy assignment or use `= delete` in C++11.
{
private:
std::vector<int> vec_;
LegacyStruct wrapped_;
}
Then anytime you need to use values, just assign it to &vec_[0]. This will stay constant if/until you add more items to the vector (so you will have to use care to make sure that vector resizes don't cause problems).
Yup, you can do so - with a small trick:
struct LegacyStruct {
std::vector<int> backingStore;
int* values;
LegacyStruct(std::vector<int>& aSource) {
// Steal memory
aSource.swap(backingStore);
// Set pointer
values = &backingStore[0];
};
}
The vector.swap operation doesn't copy the ints.
Is it possible to do this without creating new data structure?
Suppose we have
struct Span{
int from;
int to;
}
vector<Span> s;
We want to get an integer vector from s directly, by casting
vector<Span> s;
to
vector<int> s;
so we could remove/change some "from", "to" elements, then cast it back to
vector<Span> s;
This is not really a good idea, but I'll show you how.
You can get a raw pointer to the integer this way:
int * myPointer2 = (int*)&(s[0]);
but this is really bad practice because you can't guarantee that the span structure doesn't have any padding, so while it might work fine for me and you today we can't say much for other systems.
#include <iostream>
#include <vector>
struct Span{
int from;
int to;
};
int main()
{
std::vector<Span> s;
Span a = { 1, 2};
Span b = {2, 9};
Span c = {10, 14};
s.push_back(a);
s.push_back(b);
s.push_back(c);
int * myPointer = (int*)&(s[0]);
for(int k = 0; k < 6; k++)
{
std::cout << myPointer[k] << std::endl;
}
return 0;
}
As I said, that hard reinterpret cast will often work but is very dangerous and lacks the cross-platform guarantees you normally expect from C/C++.
The next worse thing is this, that will actually do what you asked but you should never do. This is the sort of code you could get fired for:
// Baaaad mojo here: turn a vector<span> into a vector<int>:
std::vector<int> * pis = (std::vector<int>*)&s;
for ( std::vector<int>::iterator It = pis->begin(); It != pis->end(); It++ )
std::cout << *It << std::endl;
Notice how I'm using a pointer to vector and pointing to the address of the vector object s. My hope is that the internals of both vectors are the same and I can use them just like that. For me, this works and while the standard templates may luckily require this to be the case, it is not generally so for templated classes (see such things as padding and template specialization).
Consider instead copying out an array (see ref 2 below) or just using s1.from and s[2].to.
Related Reading:
Are std::vector elements guaranteed to be contiguous?
How to convert vector to array in C++
If sizeof(Span) == sizeof(int) * 2 (that is, Span has no padding), then you can safely use reinterpret_cast<int*>(&v[0]) to get a pointer to array of int that you can iterate over. You can guarantee no-padding structures on a per-compiler basis, with __attribute__((__packed__)) in GCC and #pragma pack in Visual Studio.
However, there is a way that is guaranteed by the standard. Define Span like so:
struct Span {
int endpoints[2];
};
endpoints[0] and endpoints[1] are required to be contiguous. Add some from() and to() accessors for your convenience, if you like, but now you can use reinterpret_cast<int*>(&v[0]) to your heart’s content.
But if you’re going to be doing a lot of this pointer-munging, you might want to make your own vector-like data structure that is more amenable to this treatment—one that offers more safety guarantees so you can avoid shot feet.
Disclaimer: I have absolutely no idea about what you are trying to do. I am simply making educated guesses and showing possible solutions based on that. Hopefully I'll guess one right and you won't have to do crazy shenanigans with stupid casts.
If you want to remove a certain element from the vector, all you need to do is find it and remove it, using the erase function. You need an iterator to your element, and obtaining that iterator depends on what you know about the element in question. Given std::vector<Span> v;:
If you know its index:
v.erase(v.begin() + idx);
If you have an object that is equal to the one you're looking for:
Span doppelganger;
v.erase(std::find(v.begin(), v.end(), doppelganger));
If you have an object that is equal to what you're looking for but want to remove all equal elements, you need the erase-remove idiom:
Span doppelganger;
v.erase(std::remove(v.begin(), v.end(), doppelganger)),
v.end());
If you have some criterion to select the element:
v.erase(std::find(v.begin(), v.end(),
[](Span const& s) { return s.from == 0; }));
// in C++03 you need a separate function for the criterion
bool starts_from_zero(Span const& s) { return s.from == 0; }
v.erase(std::find(v.begin(), v.end(), starts_from_zero));
If you have some criterion and want to remove all elements that fit that criterion, you need the erase-remove idiom again:
v.erase(std::remove_if(v.begin(), v.end(), starts_from_zero)),
v.end());