While passing iterators into a function, i think it is easier to work with a vectors, so i convert with constructor, but, looks like new vector created. And so, no changes returned from a function. But i need it to be changeble and not take new memory.
template <class Element>
void HeapSort::MaxHeapify(typename vector<Element>::iterator first, typename vector<Element>::iterator last, ...)
{
vector<Element> array(first, last);
...
}
Also, when calling such a function i must explicitely point type of Element. Why?
MaxHeapify<Element>(array.begin(), array.end(), i);
EDIT: with easier i mean that *(first + i) = 5 is not so easy to write and then read as array[i] = 5.
EDIT 2: It is appeared that it can be written as first[i] = 5. May be somebody can me help with second part of the question?
There's no need to construct a new vector inside your function. You can do the algorithm with just the iterators. But if you want to do that because it's "easier", you'll have to copy the values from the resulting array to the parameter range after you're done with the vector. You can't avoid allocating new memory with your approach.
As for the second question, the compiler can't deduce the Element type. You can fix that by declaring the template like this:
template <class Iter>
void HeapSort::MaxHeapify(Iter first, Iter last, ...)
As a bonus, this allows the function to work with any type of random access iterators.
Related
Suppose I have a templated function that takes various kinds of vectors (but for various reasons I can't mention this in the template parameter). Here's what I'm trying to do: insert a new, default constructed element at a specific spot, without knowing its type:
template <typename T>
void foo(T* v) {
v->insert(v->begin() + 5, decltype(v->at(0))());
}
This doesn't work, but gives you an idea of what I'm trying to do. I also tried to use value_type from std::vector but I ran into problems there as well. Any ideas how to solve this problem?
Sidestep the whole "name the type" business:
v->emplace(v->begin() + 5);
or
v->insert(v->begin() + 5, {});
Your current version doesn't work because decltype(v->at(0)) is a reference type. value_type should work if you use it correctly, but without seeing what you are doing I can't say what's wrong with it.
If you know v is always a std::vector of some element type, then just type it that way to begin with, so T is the element type, not the vector type:
template <typename T>
void foo(std::vector<T>* v) {
v->insert(v->begin() + 5, T());
}
That also ensures that v->insert() and v->begin() + 5 are valid statements. You original code allowed anything to be passed for v, so there was no guarantee that v->insert() and v->begin() exist, or that begin() returns a random-access iterator that accepts + 5.
Simple question. I have a pointer to an array.
vector<int> myVector = { 22, 18, 12, -4, 58, 7, 31, 42 };
int* myPtr = myVector.data();
I also have a function that takes a reference to an array as a parameter.
template<typename T> void sort_quick(T (&arr)[]);
How can I pass my vector's array to this function without having to copy the potentially huge array in data().
sort_quick(*arr); // No matching function call for 'sort_quick'
Also, I need to pass it as an array, it's a pre-requisite, so don't come talking about just passing the vector because I wish I could.
Edit:
template<typename T, int N> void sort_quick(T (&arr)[N]);
This should now be legal syntax?
Edit2:
template<typename T> void sort_quick(T* arr, size_t length);
I believe this would be the best version then, when needing to deal with arrays and not vectors.
C-style array bounds must be known at compile-time. Your original definition of sort_quick was illegal. This is legal:
template<typename T, int N> void sort_quick(T (&arr)[N]);
however it can only be called with an actual array.
In order to support sorting containers whose sizes are not known until compile-time, you will need to make a version that takes two parameters. These could be two pointers, or a start pointer and a length. The version taking array could be made to delegate to the new version.
The idiomatic way would be to use iterators:
template<typename Iterator> void sort_quick(Iterator begin, Iterator end);
and then you can call the function like:
sort_quick(myVector.begin(), myVector.end());
The signature for your sorting function takes an array reference; Something you may not know is that no rvalue expression in C or C++ may yield an array, that is to say, an rvalue may not be of an array type. You may also not know about how arrays can decay in to pointers when passed as parameters to functions, but that is perhaps a little off topic;
Regardless, you may not do something like:
sort_quick(my_vec.data());
If you absolutely must use that function with that signature (i.e. it is an interface over which you have no control), you will need to construct an array using the information that the set of values is [my_vec.data(); my_vec.size()] is valid. As to why you can't just use std::vector::data(), if you look carefully, you will see it's return type is T*, and pointers will not magically reverse-decay in to array references.
If you don't use C++11 you can do it that way:
sort_quick( &arr[0] );
I am not crystal clear about pointer and iterator, especially in the case of constructing vector from an array.
From the description about vector, it says
// the iterator constructor can also be used to construct from arrays:
int myints[] = {16,2,77,29};
std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
in primary, it is:
#include <vector>
int arr[ARR_LEN] = { /* Array elements */ };
std::vector<int> vecInt(arr, arr + ARR_LEN);
does it use the following constructor?
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
If so, then here, is a pointer (for array) treated into an random access iterator, then as input iterator? How does the compiler do this? Considering iterators from other containers, what is the type of this iterator, e.g. "array[int]"::iterator?
int arr[N] = {};
for (int i= 0; i<N; i++) {}
instead of the above, can I do something like as follows?
for (ItorType it=arr; it!=arr+N; ++it) { ... }
The iterator template arguments are not specific class. In fact, the names have no real impact on the compilation process. Instead, they refer to specific concepts which specify the methods available for a specific iterator. Any RandomAccessIterator also has to be an InputIterator: the RandomAccessIterator concept is a refinement of the InputIterator concept.
The compiler really just passes these arguments, in your case pointers, through. Pointers implement the RandomAccessIterator concept, i.e., they can be readily used with the std::vector<...> constructor you quoted. BTW, the proper way to write this initialization is
std::vector<int> vecInt(std::begin(arr), std::end(arr));
The function templates begin() and end() were added to the latest version of C++ and deduce the size of statically sized arrays. Both approaches used to determine the size in your example are problematic in some ways.
does it use the following constructor?
Yes.
If so, then here, is a pointer (for array) treated into an random access iterator, then as input iterator?
Yes.
How does the compiler do this?
Concepts like InputIterator are informal specifications of how a type must behave; to be an InputIterator, it must support indirection (*it) to access a value, and increment (++it and it++) to move to the next value. To be a RandomAccessIterator, it must support these operations, plus a few more such as addition and subtraction. Pointers meet these requirements, so can be used wherever an iterator is required.
Considering iterators from other containers, what is the type of this iterator, e.g. "array[int]"::iterator?
You'd use a pointer, int*, to iterate over an array, int[]. When used as a function argument (as here), or used in arithmetic (such as arr+N), the array is automatically converted to a pointer to its first element.
instead of the above, can I do something like as follows?
Yes. ItorType would be the corresponding pointer type, int*. In C++11, we have begin and end functions to get the beginning and end of any range including arrays:
for (auto it = std::begin(arr); it != std::end(arr); ++it)
and also range-based loops:
for (int i : arr)
Yes, it uses aforementioned constructor and treats pointer as random access iterator. Since all RandomAccessIterators also are InputIterators (see here). Compiler can do this cause Iterator classes defines valid operators on itself in terms of static interfaces. No matter how they implemented and actually means. Only type correctness and interface correctness plays their roles.
You could typedef int * ItorType and your last sample would compiled and worked very well.
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
The compiler will use this constructor simply because it a two argument constructor with the same type for both arguments.
It doesn't have to be that the arguments are iterators. For instance if you write
vector<int> x(3.0, 1.0);
the compiler will try to use the same constructor (which would then lead to compile errors because doubles can't be used like iterators).
Some may or may not know that you can get the size of an array argument to a function using this code:
template<typename DataType, size_t SIZE>
void SortingAlgorithm(DataType (&array)[SIZE])
{
...
return;
}
where SIZE can be used to represent the number of elements in the array, allowing the, programmer using your function to pass the array as an argument without explicitly passing the length. For example, the programmer can do this:
SortingAlgorithm( arrayToBeSorted ); //the length is not passed here, which is fine
For Algorithms that can be implemented relatively easily in an iterative style, this is fine. But I've tried to do this with other algorithms that are recursive. The code for each one might look something like this:
template<typename DataType, size_t SIZE>
void SortingAlgorithm(DataType (&array)[SIZE])
{
DataType newArray[SIZE];
memcpy(newArray,array, SIZE); //copy to a new array
SortingAlgorithm( newArray );
...
return;
}
But this throws an error every time saying that the program expects a different argument type, type conversion has failed, and shows an attempt to typecast the SIZE specifier for the newArray array multiple times, and the program fails. It does not however spit out these errors if I use an actual value to define the size of newArray before making the recursive call, like this:
DataType newArray[10]; //arbitrary number, but the compiler accepts this.
Why is it that a variable sized array causes an error? Also is there anyway to implement a recursive algorithm that accepts an array as input but does not require the length of the array as an argument because It can determine the length of the array each time within the function call?
Make a helper function that takes a size, it can be used internally by your other function, and nobody has to know about it. For example:
template<typename DataType>
void SortingAlgorithm_helper(DataType * ptr, size_t size)
{
...
SortingAlgorithm_helper(ptr + 1, size - 1);
...
}
template<typename DataType, size_t SIZE>
void SortingAlgorithm(DataType (&array)[SIZE])
{
...
SortingAlgorithm_helper(newArray,SIZE);
...
}
By your comments, you are considering switching to vector. Well, you don't have to make a choice here. You can make the code much more generic to handle both. Instead of passing in a pointer and a size to the helper function, we pass in two iterators designating a range. Then we modify the main function to accept any container, as long as std::begin and std::end work on it.
template<typename Iterator>
void SortingAlgorithm_helper(Iterator first, Iterator last)
{
...
SortingAlgorithm(++first, last);
...
}
template<typename Container>
void SortingAlgorithm(Container & c)
{
...
SortingAlgorithm_helper(std::begin(c), std::end(c));
...
}
This should handle built-in arrays, std::vector, std::array and std::deque. If you limit the operations performed on the iterators to bi-directional (++ and --), then it should handle std::list as well.
Don't do this. Instead, use one of the following options:
Use a std::vector (it has a size() method) instead of an array.
Pass in the size of the array as an additional argument to your function.
As for recursive algorithms, don't just copy the elements into a new array, it's time consuming.
Instead, pass in two pointers to the beginning and the end of the portion of the array that you want to process (look at how STL algoritms work, std::sort doesn't take a container, instead it takes two iterators).
Note however, that this actually depends on the details of the algorithm you're using, some algorithms may actually require explicit copying, but you should avoid it when possible.
The template generates code at compile time, specifying a value like 10 allows it to generate code because it knows what value to use. Specifying something that can not be determined at compile time(like a variable that only has a value at run time) means it doesn't know what value to use when generating code.
The compiler needs to figure out the sizes at compile time, not run time. My compiler accepts your code as written, but apparently yours can't figure out the size. You can explicitly tell it the size like this:
SortingAlgorithm<DataType, SIZE>( newArray );.
Both versions of that line of code work with my compiler.
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".