How can a vector be used in this way? - c++

Can someone explain this code please? how is it that the function bar accepts a reference to the first element of the vector?
jintArray arry;
std::vector<int> foo = GetIntegerArray(env, arry);
bar(&foo[0])
where the protoytpe of bar is
bar(int* array)

This is valid as long as the template type isn't bool. The C++ vector type specifies that the vector elements are consecutive in memory like that so that you can do exactly this.
The reason why it doesn't work with bool is due to template specialization. Where the bools are compressed down to a bitfield.
http://en.wikipedia.org/wiki/Vector_%28C%2B%2B%29#vector.3Cbool.3E_specialization

how is it that the function bar accepts a reference to the first element of the vector?
This seems to be the source of the confusion. The expression &foo[0] is not a reference to the first element, but rather a pointer. operator[] is overloaded in the vector class to obtain a reference (or const-reference), and applying & will obtain the address of the object.

Yes. Just make sure the vector is not empty, or &foo[0] would be an error. C++11 introduced the std::vector<T>::data() function that does not have this problem.
Also, returning a vector by value is usually not a good idea. You might want to use an output iterator or a vector reference parameter in GetIntegerArray, so you would call it like this:
std::vector<int> foo;
GetIntegerArray(env, arry, back_inserter(foo));
or
std::vector<int> foo;
GetIntegerArray(env, arry, foo);

When you use std::vector<int>, it is guaranteed that all the element are created in contiguous memory. As such, when you write &v[0] it returns the pointer to the first element, and from this you can go the next element by writing &v[0]+1, and so on.
By the way, if you want to traverse through all elements or a section of elements, then a better interface for bar would be this:
void bar(int *begin, int *end)
{
for ( ; begin != end; ++begin)
{
//code
}
}
So you can call like this:
bar(&foo[0], &foo[0] + foo.size());//process all elements
bar(&foo[0], &foo[0] + foo.size()/2);//process first half elements
bar(&foo[0], &foo[0] + N); //process first N elements(assumingN <=foo.size())
bar(&foo[0]+foo.size()/2, &foo[0]+foo.size());//process second half elements

Related

Creating a vector<int> reference to an integer array pointer (C++)

My goal is to take 3 standard integer arrays and use vector methods to manipulate them without having to copy all the array elements into a separate vector array.
The reason I want to do this without copying array elements is because these will be very large arrays and efficiency is a priority.
Here are two examples of what I'd like it to look like:
Simplest example:
int arr[10000];
int *ptr;
std::vector<int> & v = ptr;
v.push_back(10);
Function example:
// Function that inputs 3 integer arrays and creates pointers to them.
void use_vector(int *dst, int *src1, int *src2) {
//creates vector references (without copying the original array elements)
std::vector<int> & v_dst = dst;
std::vector<int> & v_src1 = src1;
std::vector<int> & v_src2 = src2;
// do something using vector methods:
v_dst.push_back( v_scr1.size() + v_scr2.size() );
}
In both cases, I get this error message:
error: invalid initialization of reference of type ‘std::vector<int>&’ from expression of type ‘int*’
std::vector<int> &v = ptr;
Ideally the vectors are references (std::vector &), but they could also be pointers (std::vector *) if references aren't possible.
You can't do it because std::vector is designed with ownership semantics on the data it holds.
This means that it is not designed to wrap an array of values and store it. It must own the data, so a copy is necessary (or a move from another std::vector).
You can't do anything like:
std::vector<int> & v = ptr;
because the left-hand side is a reference to a std::vector<int> and the right-hand side is a pointer to int.
Likewise for your second example.
Also you basically can't use a std::vector on an existing array in place. The usual way is to copy, which you say you don't want to do. Your left with defining you own class do give you the vector-like interface you want on an in place array.

Discussion about std::vector<T> and standard array

Discussion about std::vector and standard array
Say if we have following code:
void myclass::loadArray(void *outData)
void myclass::loadVector(void *outData)
void myclass::func ()
{
//here we have a vector
std::vector<int> myVector;
myVector.resize(10)
// here we have an array
int myArray[10];
here I wonder what will be the differences between following implementations
//1: array
myclass::loadArray(myArray)
//2: array
myclass::loadArray(&(myArray[0]))
//1: vector
myclass::loadVector(myVector)
//2: vector
myclass::loadVector(&(myVector[0]))
}
From my understanding, Just depending on if we want to use array and vector we pick different solution.
There is no difference between 1 and 2. Could you please correct me if I am wrong.
The two versions with the array are equivalent: in the first, the array is implicitly converted to a pointer to its first element, which the second creates explicitly.
The first version with the vector won't compile, since there is no implicit conversion to a pointer. You'll have to explictly get the address of the array; either as you do in the second version, or with vector.data().
1 and 2 will be the same for the array versions. In the first, the array will decay to a pointer to the first element, the second calls explicitly with a pointer to the first element.
The first vector version will not compile as you can't implicitly change a std::vector to a void*.
For the second vector version, you will be calling the function with a pointer to the first element stored in the vector, not the vector itself.

Delete an element from a Vector and move rest of elements down - C++

I have a vector that is of 'simpleVector' :
struct SimpleStruct
{
XMFLOAT3 hello;
XMFLOAT3 hi;
};
std::vector <SimpleStruct> simpleVector(0);
I'm trying to delete an element, such as simpleVector[3], and then move the rest of the elements down one to remove the blank space.
simpleVector.erase(std::remove_if(simpleVector.begin(), simpleVector.end(),
[](int i) { return i == 3; }), simpleVector.end());
However, I get this error: cannot convert argument 1 from
'SimpleStruct' to 'int'.
Forgive me if this is obvious, I am new to C++. How can I remove this problem?
If you want to remove the element at index 3, you can just do:
simpleVector.erase(simpleVector.begin()+3);
With std::vector, you don't need to explicitly 'move all the other elements down' - it will do that for you after the erase.
The unary predicate passed to std::remove_if needs to be able to accept a SimpleStruct. Its purpose it to evaluate whether each element of the vector should be "removed".
Your predicate accepts an int, and there is no conversion from SimpleStruct to int. You need to change your predicate to something that makes sense.
On the other hand, if you want to remove the element at simpleVector[3], all you need is
simpleVector.erase(simpleVector.begin() + 3);
To remove the element at index 3, do this instead:
simpleVector.erase( simpleVector.begin() + 3 );
Also note that you don't have to worry about moving the rest of the elements down, as the vector handles this for you automatically.
Because vectors use an array as their underlying storage, erasing elements in positions other than the vector end causes the container to relocate all the elements after the segment erased to their new positions. This is generally an inefficient operation compared to the one performed for the same operation by other kinds of sequence containers (such as list or forward_list).
– cplusplus.com

C array vs pointer : array is considered as a variable, array+0 is considered as a pointer?

I've made a container class in C++, and I have a constructor from iterators so I can write MyContainer<double> x(v.begin(), v.end()) where v is a std::vector<double>. I would like to be able to do the same with a c-array but :
double array[3] = {1., 2. , 3.};
MyContainer<double> x(array, array+3); // Doesn't work : no matching function for call to ‘MyContainer<double>::MyContainer(double [3], double*)’
MyContainer<double> x(array+0, array+3); // Work
What is the origin of the problem and how to solve it ?
Thank you very much.
Don't accept references to iterators, take them by value. It's trying to pass a reference to an array; the failing expression needs the array to decay to a pointer.
Presumably you have
template< typename Iter >
MyContainer( Iter const &first, Iter const &last );
but you need
template< typename Iter >
MyContainer( Iter first, Iter last );
Iterators need to be lightweight enough to pass by value; all the standard templates do so.
An array cannot be used as an iterator because it cannot be incremented. The storage is fixed. When you use an array in an expression like arr + 0 or pass it by value to a function, it is implicitly converted to a pointer to its first element. But that conversion doesn't happen when passing by reference.
The result of array+0 is a pointer, while array itself is not a pointer, it is an array. Your constructor does not have an overload that takes an array and a pointer, hence the compile fails.
The idiomatic way of dealing with the problem of making the beginning and the ending iterators from an array is using the begin(...) and end(...) functions:
MyContainer<double> x(std::begin(array), std::end(array));
The overload takes care of figuring out where the end of your array is, freeing you from the need to add array's length to the pointer.

Why does std::vector transfer its constness to the contained objects?

A const int * and an int *const are very different. Similarly with const std::auto_ptr<int> vs. std::auto_ptr<const int>. However, there appears to be no such distinction with const std::vector<int> vs. std::vector<const int> (actually I'm not sure the second is even allowed). Why is this?
Sometimes I have a function which I want to pass a reference to a vector. The function shouldn't modify the vector itself (eg. no push_back()), but it wants to modify each of the contained values (say, increment them). Similarly, I might want a function to only change the vector structure but not modify any of its existing contents (though this would be odd). This kind of thing is possible with std::auto_ptr (for example), but because std::vector::front() (for example) is defined as
const T &front() const;
T &front();
rather than just
T &front() const;
There's no way to express this.
Examples of what I want to do:
//create a (non-modifiable) auto_ptr containing a (modifiable) int
const std::auto_ptr<int> a(new int(3));
//this works and makes sense - changing the value pointed to, not the pointer itself
*a = 4;
//this is an error, as it should be
a.reset();
//create a (non-modifiable) vector containing a (modifiable) int
const std::vector<int> v(1, 3);
//this makes sense to me but doesn't work - trying to change the value in the vector, not the vector itself
v.front() = 4;
//this is an error, as it should be
v.clear();
It's a design decision.
If you have a const container, it usually stands to reason that you don't want anybody to modify the elements that it contains, which are an intrinsic part of it. That the container completely "owns" these elements "solidifies the bond", if you will.
This is in contrast to the historic, more lower-level "container" implementations (i.e. raw arrays) which are more hands-off. As you quite rightly say, there is a big difference between int const* and int * const. But standard containers simply choose to pass the constness on.
The difference is that pointers to int do not own the ints that they point to, whereas a vector<int> does own the contained ints. A vector<int> can be conceptualised as a struct with int members, where the number of members just happens to be variable.
If you want to create a function that can modify the values contained in the vector but not the vector itself then you should design the function to accept iterator arguments.
Example:
void setAllToOne(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
std::for_each(begin, end, [](int& elem) { elem = 1; });
}
If you can afford to put the desired functionality in a header, then it can be made generic as:
template<typename OutputIterator>
void setAllToOne(OutputIterator begin, OutputIterator end)
{
typedef typename iterator_traits<OutputIterator>::reference ref;
std::for_each(begin, end, [](ref elem) { elem = 1; });
}
One big problem syntactically with what you suggest is this: a std::vector<const T> is not the same type as a std::vector<T>. Therefore, you could not pass a vector<T> to a function that expects a vector<const T> without some kind of conversion. Not a simple cast, but the creation of a new vector<const T>. And that new one could not simply share data with the old; it would have to either copy or move the data from the old one to the new one.
You can get away with this with std::shared_ptr, but that's because those are shared pointers. You can have two objects that reference the same pointer, so the conversion from a std::shared_ptr<T> to shared_ptr<const T> doesn't hurt (beyond bumping the reference count). There is no such thing as a shared_vector.
std::unique_ptr works too because they can only be moved from, not copied. Therefore, only one of them will ever have the pointer.
So what you're asking for is simply not possible.
You are correct, it is not possible to have a vector of const int primarily because the elements will not assignable (requirements for the type of the element contained in the vector).
If you want a function that only modifies the elements of a vector but not add elements to the vector itself, this is primarily what STL does for you -- have functions that are agnostic about which container a sequence of elements is contained in. The function simply takes a pair of iterators and does its thing for that sequence, completely oblivious to the fact that they are contained in a vector.
Look up "insert iterators" for getting to know about how to insert something into a container without needing to know what the elements are. E.g., back_inserter takes a container and all that it cares for is to know that the container has a member function called "push_back".