Binary Search on a std::string array - c++

Below is the code and
std::string str[5] = {"Tejas","Mejas","Rajas","Pojas","Ljas"};
std::sort(str,str+5);
size_t test = bin_search("Ljas",str,5);
Here is the generic function for binary search
template<class T>
size_t bin_search(T x, T* array, int n)
{
size_t begin = 0, end = n;
// Invariant: This function will eventually return a value in the range [begin, end]
while (begin != end) {
size_t mid = (begin + end) / 2;
if (array[mid] < x) {
begin = mid + 1;
} else {
end = mid;
}
}
return begin; // Or return end, because begin == end
}
And the Error is
main.cpp|12|error: no matching function for call to 'bin_search(const char [5], std::string [5], int)'|
There is a problem with only the std::string array, but the int array works really fine.
Does it work with string arrays or is there anything missing in the logic?

As the error message tried to tell you, "Ljas" is not std::string, it's const char[5]. Then the template argument deduction failed since the type T could not be deduced (as const char* or std::string).
You could explicitly cast it to std::string to make template argument deduction work well:
size_t test = bin_search(std::string("Ljas"),str,5);
or explicitly specify the template argument to avoid template argument deduction:
size_t test = bin_search<std::string>("Ljas",str,5);

template<class T>
size_t bin_search(T x, T* array, int n)
Expects that you recieve a T and a pointer to T. When the compiler deducts the types in
size_t test = bin_search("Ljas",str,5);
x is deduced as a const char[5] as all string literals have the type const char[N]. array is deduced std::strign[5]. Since a cont char[] and a std::string[] are not the same type the no function will be generated. You need to make "Ljas" a string like
size_t test = bin_search(std::string("Ljas"),str,5);
Also note that the collection passed to a binary search needs to be sorted. If the data is not sorted then you cannot reason what half the element should be in.

size_t test = bin_search(std::string("Ljas"), str, 5); maybe?

You algorithm won't work even if you fix your compile error because a binary search requires a sorted array.
It isn't necessary to write your own binary search algorithm, the STL already has one: http://en.cppreference.com/w/cpp/algorithm/binary_search
Your first argument should be a std::string, not a string literal: bin_search(std::string("Ljas"),str,5);

Related

const pointers passed as argument into constexpr function

I am doing some very basic problems to get familiar with Template Metaprogramming. This one in particular is just to add up the values on the down-right diagonal of a square matrix (indexes of [0,0],[1,1], etc.). I have solved this in many ways with different constexpr functions, but I do not understand why one particular way fails. Code and error message below:
constexpr int DRsum(const int* starter, const size_t size, int itr = 0)
{
if(itr==size-1)
{
return *starter;
}
size_t sum = 0;
sum = *starter;
sum += DRsum(starter+size+1, size, itr+1);
return sum;
}
int main()
{
static const size_t size = 3;
static constexpr const int matrix[][size] = {
{1,2,3},
{4,5,6},
{7,8,9}
};
static constexpr int const* baseptr = &matrix[0][0];
constexpr int sum = DRsum(baseptr, size);
}
The error message is as follows:
main.cpp|69| in constexpr expansion of 'DRsum((& matrix[0][0]), 3u, 0)'|
main.cpp|39| in constexpr expansion of 'DRsum((starter + ((((sizetype)size) + 1u) * 4u)),
((size_t)size), (itr + 1))'|
main.cpp|69|error: '* starter' is not a constant expression|
I am not sure why this is doing this, as I am new to Template Metaprogramming. I made a dummy function to test if the pointer dereference was the issue, and that worked out fine. My guess is that it might have to do with me passing in a pointer rather than a pointer to a constexpr pointer, but I'm not sure. Any help would be appreciated, Thanks.
Also, I have already solved this by means of just passing in the entire structure (just straight up a deep copy) but I would rather know how to pass things by shallow copy or pointer into constexpr functions. Thanks.
A 2D array (matrix) cannot be treated as a 1D array, so starter+size+1 results in undefined behavior. Usually undefined behavior is undefined so the program may work as expected, but when an undefined behavior happens within a structure that requires a constant expression, the program becomes ill-formed thus the compiler emits an error.
I don't think there is a simple fix because there is no well-defined way to access the whole 2D array through starter (even with std::launder), unless you change the signature of DRsum. For example, you can write DRsum as follows:
template <size_t size>
constexpr int DRsum(const int (*starter)[size], int itr = 0)
{
if (itr == size - 1)
{
return (*starter)[itr];
}
size_t sum = 0;
sum = (*starter)[itr];
sum += DRsum(starter + 1, itr + 1);
return sum;
}
then call it like constexpr int sum = DRsum(matrix);.

Using C++11's begin() and end() function to determine array dimension by parameter

So I am currently learning C++ (with previous experience in Java and JavaScript) and as far as I am concerned you can't pass an array as argument in C++ like you can in Java. But you can pass a pointer to the first element in the array. So I could iterate through an array like this:
bool occurs(int* arrInt, int length, int sought, int& occurrences)
{
for (int i = 0; i <= length; ++i)
{
if (arrInt[i] == sought)
occurrences++;
}
// if occurences > 0 return true, else false
return occurrences;
}
The whole function should basically return a boolean telling me wether the given int (sought) was found in the array (arrInt) or not. Also I am supplying a little counter via reference (occurrences).
But what bugs me is the length parameter. C++11 provides those fancy std::begin / cbegin() and std::end / cend() functions to get the first and one past the last element of an array:
int arr[] = {1,2,3,4} // arr is basically a pointer to an int, just as the
// function parameter of ocurs(int*,int,int,int&)
auto end = std::end(arr); // end points to one past last element
But why can't I use my arrInt parameter as argument for that function? Then i could get rid of the length parameter:
bool occurs(int* arrInt, int sought, int& occurences)
{
for (auto it = std::begin(arrInt); it != std::end(arrInt); ++it)
{
if (*it == sought)
occurences++;
}
// if occurences > 0 return true, else false
return occurences;
}
Am I missing a major concept here? Thanks in advance
In your first example:
int arr[] = {1,2,3,4} // arr is basically a pointer to an int, just as the
// function parameter of ocurs(int*,int,int,int&)
auto end = std::end(arr); // end points to one past last element
arr is NOT "basically a pointer to an int". arr is of type int[4]. Note that the length is part of the type. As a result, the compiler can easily determine where "one past last element" is. Just add the length.
Where the confusion may come in is that arr is convertible to (you'll sometimes hear decays to) int*. But it isn't just a pointer.
In your second example:
bool occurs(int* arrInt, int sought, int& occurences)
{
for (auto it = std::begin(arrInt); it != std::end(arrInt); ++it) {
...
}
...
}
arrInt is just a pointer. As such, how can you know where end() is? There's no information here. That's why you need that extra length parameter.
You can instead pass in the full array, but you have to do it by reference (you cannot pass arrays by value, thanks C!). And to do that, you have to make it a function template:
template <size_t N>
bool occurs (int (&arrInt)[N], int sought, int& occurrences) {
...
}
Here, arrInt is an array - and its length is encoded in the type (N). So you can write std::end(arrInt).
occurs() is basically rewriting std::count, so you could just use that instead:
int arr[] = {1, 2, 3, 3, 8};
int occurrences = std::count(std::begin(arr), std::end(arr), 3); // yields 2
Or, even simpler, use std::vector<int>.
First, note that an array is not a pointer. And so in this example code:
int arr[] = {1,2,3,4} // arr is basically a pointer to an int, just as the
// function parameter of ocurs(int*,int,int,int&)
… the comments are simply wrong.
However, in both C and C++ an array type expression decays to pointer type, with pointer to first item as result, in a context where a pointer is expected. An example that is not such context, is where an array is passed by reference. Another example is when it's used as argument to sizeof.
With arrInt declared as
int* arrInt
it's just a pointer, with no information about whether it points to a single int or to somewhere in an array, and so
std::end(arrInt)
can't deduce an array size. Normally it deduces that from the array type of the argument. Or from a container's size or end member (how it's implemented is unspecified, and the same info is available several ways).
One possibility is to change your function design, e.g. change it to accept two pointers (or general iterators), like std::find.
Another possibility is to use std::find in your function.
You can do that because given a start pointer and an array size, you can trivially compute the past-the-end pointer for the array, to use as argument to std::find.

Variadic Function Universal Sentinel Value

I am trying to write a variadic function that takes an unknown number of arguments and creates an array of the input type (Its a template function so if the input arguments are floats, it returns a float ptr and likewise for other data types)
I was thinking that I can use either NAN or INFINITY as a sentinel value because one of them theoretically would never be used in an array (at least for my purposes).
More or less the function looks like this
template<typename T>
T* arrayIt(T first, ...)
{
va_list ap;
va_start(ap, first);
T n = first, *array = (T*)malloc(sizeof(T));
int sz = 0;
while(!(isnan(n)) /* or infinite(n) */){
sz++; // inc array size
array = realloc(array, sizeof(T)*(sz+1)); // realloc
array[sz] = n;
n = va_arg(ap, T); // update temp
}
array[0] = sz; // store size of array
return (array + 1); //doing this places the first element at array[0]
// the size of the array is stored at array[-1]
}
The function works as planned with everything except ints.
I would like to know how to use the function this way using NAN as a sentinal.
I would also like to do this preferably without a macro.
i.e. int * a = arrayIt<int>(1,2,3,4,5,6,NAN);
Might not answer the exact question you're asking, but in C++11, you can just do this:
template <typename T, typename... Vals>
std::array<T, sizeof...(Vals)> arrayIt(Vals... vals) {
return std::array<T, sizeof...(Vals)>{static_cast<T>(vals)... };
}
To be used without a sentinel, like:
auto a = arrayIt<int>(1, 2, 3, 4, 5, 6);

error C2893: Failed to specialize function template

I am getting this error and another error too ** "IntelliSense: no instance of function template matches the argument list"** when compiling the following code
I know there might be logic mistakes in my function but I need to solve this error first to be able to debug my function .
#include <iostream>
using namespace std;
template<class T>
T myMax (T& arr ,int arrStart ,int arrSize)
{
if(arrStart==arrSize-1)
return arr[arrSize];
int median = (arrStart+arrSize)/2 ;
T left , right , maximum ;
left = max(arr,arrStart , median);
right = max(arr , median+1 , arrSize-1) ;
if (left>right)
maximum = left;
else
maximum = right ;
return maximum ;
}
void main()
{
int arrSize = 5;
int arr[] = {1,3,4,6,22};
int x;
x = myMax(arr,0,arrSize);
}
The argument for parameter arr is of type int[5]. Since you didn't specify a template argument for T when calling myMax, argument deduction happens and T is deduced to be int[5].
Then the compiler attempts to specialize the function template with T = int[5] (i.e., it tries to replace all instances of T with int[5]). This fails because the function returns a T by value but it is not possible to return an array (like int[5]) by value.
It looks like you want T to be the element type. If that is the case, you can explicitly take a reference to the array, e.g.,
template<class T, unsigned N>
T myMax (T (&arr)[N])
Though, a more idiomatic way to write the function would be to have it take a pair of random access iterators and have it return an iterator pointing to the "max" element:
template <typename RandomAccessIt>
RandomAccessIt myMax (RandomAccessIt first, RandomAccessIt last)
first is an iterator to the first element in the range and last is an iterator to one-past-the-end of the range, as is idiomatic for the STL algorithms. Pointers are usable as random access iterators, so this function can be called as
int* pointerToMaxElement = myMax(arr, arr + arrSize);
The advantage of the iterator approach is that it works with any random access range, including an array, std::vector, std::array, and std::deque.
From a quick look, the two things that jump out at me are:
You're using T in different ways in the template function. You're returning a T object, and taking a reference to a T object as an argument - but when you use it, you're passing an an int array as the argument but expect just an int returned
You don't call your template function with any template (ie, myMax<int>(...)) Edit - as Mark B points out, this isn't required however

Templates, STL, C++

I wrote this routine to order items, keep only unique items, where it takes in an array of type T, and the size of the array. It returns the new size of the array after processing.
template <class T>
int reduce(T array[], int size) {
T *begin = array;
T *end = array + size;
sort(begin, end);
T *end_new = unique(begin, end);
return end_new - array;
}
My question is I was expecting it to sort const char *data like
{"aa", "bb", "bc", "ca", "bc", "aa", "cc", "cd", "ca", "bb"};
into //aa bb bc ca cc cd
However it does it the opposite way, : "cd cc ca bc bb aa"
Why does it do that? Does it not use the standard string comparisons? If I wanted to, how could I alter it so it would order const char * alphabetically? thanks.
sort() uses operator< per default, which would just compare the addresses in your case.
If you want to sort C-strings, you have to pass a comparator to sort(). To do this generically you can let the user pass a comparator, use specialization on a comparator function or a combination of these:
template<class T> bool my_comp(T a, T b) {
return a < b;
}
template<> bool my_comp<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) < 0;
}
template<class T, class Comp>
int reduce(T array[], size_t size, Comp comp = my_comp<T>) {
// ...
std::sort(begin, end, comp);
// ...
}
std::sort uses < by default, < on chars has nothing to do with their lexicographic ordering. You can provide an additional parameter to sort to tell it how to compare, or you can use an array of std::string or similar instead of char.
const char*'s < operator performs pointer comparison, not string data comparison. Either use std::string for your string data, or specialize reduce so that it calls sort with a special comparator based on strcmp. I'm guessing you got the output you did because your compiler decided to reverse-alphabetize all of its string constants in memory.
unique also isn't doing anything -- it only works in the first place because your compiler pooled all of the strings in memory at compile time, so that all of the "bb" strings would use the same memory. If you read the exact same strings from a file, your array wouldn't change. The solutions to this problem are the same.