Binary search function with templates, for arrays - compilation problem - c++

To preface my problem: in my project, i have to implement a few functions manually, since i can't use the standard library functions for various reasons.
I have beginner experience with templates so far so some things are still not clear to me.
For this i wanted to implement a simple binary search function:
template<class T, class TValue, ptrdiff_t compare(const T &elem, const TValue &value)>
bool binary_search(T const array[], size_t n, const TValue &value, size_t &index) {
size_t indexLow = 0;
size_t indexHigh = n;
while (indexLow < indexHigh) {
size_t indexMid = indexLow + ((indexHigh - indexLow) / 2);
if (0 > compare(array[indexMid], value)) {
indexLow = indexMid + 1;
continue; //search right
}
indexHigh = indexMid; //search left
}
if ((n > indexLow) && (0 == compare(array[indexLow], value))) {
index = indexLow;
return true;
}
return false;
}
I know it is a bit unorthodox to NOT use iterators or overloaded operators to compare, but i wanted to keep the algorithm itself as simple as possible.
Also it is important for me, that i works with simple arrays (it does not need to work on containers like vector and such).
Using only for size_t, it works perfectly with the following compare function:
ptrdiff_t cmp_size_t(const size_t &elem, const size_t &value) {
if (elem > value)
{
return 1;
}
if (elem < value)
{
return -1;
}
return 0;
}
Example of usage:
size_t test_array0[] = { 1,2,3,4,5,6,7,8 };
size_t n_array0 = sizeof(test_array0) / sizeof(test_array0[0]);
size_t index;
for (size_t search_val = 0; 13 > search_val; ++search_val) {
if (binary_search<size_t, size_t, cmp_size_t>(test_array0, n_array0, search_val, index)) {
std::cout << search_val << " found at index: " << index << std::endl;
}
else {
std::cout << search_val << " not found" << std::endl;
}
}
I would also like to be able to search array of object and array of object pointers too, and this is where i have stumbled into a problem which i have been trying to solve for days now.
Take the following class with a compare function:
struct TestClass {
const void *const value;
TestClass(const void *Value) :value(Value) {}
static ptrdiff_t cmp(const TestClass *&ArrayElem, const void *&SearchValue) {
if (ArrayElem->value > SearchValue) {
return 1;
}
if (ArrayElem->value < SearchValue) {
return -1;
}
return 0;
}
};
If i try to use the same code to search and array:
TestClass testElem0((void *)1);
TestClass testElem1((void *)2);
TestClass testElem2((void *)3);
TestClass *test_array1[] = {
&testElem0,
&testElem1,
&testElem2,
};
size_t n_array1 = sizeof(test_array1) / sizeof(test_array1[0]);
for (size_t search_val = 0; 13 > search_val; ++search_val) {
if (binary_search<TestClass *, void *, TestClass::cmp>(test_array1, n_array1, (void *)search_val, index)) {
std::cout << search_val << " found at index: " << index << std::endl;
}
else {
std::cout << search_val << " not found" << std::endl;
}
}
i get the following compilation error:
1>d:\temp\count_test\count_test\main.cpp(78): error C2672: 'bynary_search': no matching overloaded function found
1>d:\temp\count_test\count_test\main.cpp(78): error C2893: Failed to specialize function template 'bool bynary_search(const T [],std::size_t,const TValue &,size_t &)'
1> d:\temp\count_test\count_test\main.cpp(78): note: With the following template arguments:
1> d:\temp\count_test\count_test\main.cpp(78): note: 'T=TestClass *'
1> d:\temp\count_test\count_test\main.cpp(78): note: 'TValue=void *'
1> d:\temp\count_test\count_test\main.cpp(78): note: 'compare=ptrdiff_t TestClass::cmp(const TestClass *&,const void *&)'
The reason i`m passing by reference is because sometimes i want to search in array of objects, or array of object pointers, and i want to make sure it is not possible to write code, that invokes the copy constructor. I know, that for atomic types like size_t, int, void *, passing by reference is not the best way to do it, but since i'm also passing elements with const, the compiler will know to optimize it.
Also another thing i'm not sure, is if is it possible to deduce some of the template arguments from the function arguments, so i can write:
binary_search<cmp_size_t>(test_array0, n_array0, search_val, index)
instead of
binary_search<size_t, size_t, cmp_size_t>(test_array0, n_array0, search_val, index)

Your template expects a const T& and const TValue&.
When declaring references to pointers the placement of the const matters.
const TestClass *&ArrayElem is a reference to a const pointer.
TestClass* const &ArrayElem is a const-reference to a pointer.
Only the latter will be accepted by your template.

Related

Incompatible conversion while assigning even tho i catch it with an if statement C++11

template <typename T> using Map = std::map<std::string, T>;
class object_index {
public:
object_index(){};
int cntr = 0;
Map<int> MapInt;
Map<bool> MapBool;
Map<std::string> MapStr;
Map<double> MapDouble;
template <typename V> void insert2Map(V lhs) {
if (std::is_same<V, int>::value) {
std::cout << "INT: ";
std::string key = std::to_string(cntr);
MapInt[key] = lhs; // ERROR
/* incompatible pointer to integer conversion assigning to
'std::map<std::basic_string<char>, int>::mapped_type'
(aka 'int') from 'const char *' */
}
if (std::is_same<V, const char *>::value) {
std::cout << "STR: ";
}
if (std::is_same<V, bool>::value) {
std::cout << "BOOL: ";
}
if (std::is_same<V, double>::value) {
std::cout << "DOUBLE: ";
}
std::cout << lhs << std::endl;
}
template <typename Ob> object_index &operator,(Ob obj) {
insert2Map(obj);
++cntr;
return *this;
}
// o1[values 1.3, 2, 4, "hello"];
};
class object {
public:
void operator[](object_index index) { item = index; }
private:
object_index item;
};
#define values object_index(),
int main(void) {
object o1 = object();
o1[values 1.3, 2, true, "hello"];
return 0;
}
DISCLAIMER:: I know there is no reason to use this weird syntax , its for a Uni project that asks for this.
It should never get an argument of an incompatible type , I check for it before using it. How can i get past this? Or is the compiler right? How can I achieve this correctly?

Overloading operator += with array c++

I am doing a c++ program. Here's what I have to do: I create an array of the size I want. The array is auto-filled with 0.
With the operator += i have to insert 1 at the place i chose.
Example :
array += 2;
will insert 1 at the index 2 of my array.
But how can I do it ?
My .h file
#ifndef BITARRAY_H
#define BITARRAY_H
#include <ostream>
class bitArray
{
public:
bitArray(int n);
virtual ~bitArray();
bitArray& operator+=(const bitArray&); //this operator
bitArray& operator-=(const bitArray&);
int& operator[] (int x) {
return sortie[x];
}
protected:
private:
int sortie[];
int n;
};
//ostream& operator<<(ostream&, const bitArray&);
#endif // BITARRAY_H
My method in the cpp file :
bitArray& bitArray::operator+=(const bitArray& i)
{
this ->sortie[i] = 1;
return *this;
}
But it does not work. Am I doing the right way?
My error is :
no match for 'operator[]' (operand types are 'int [0]' and 'const bitArray')|
Thank you in advance !
no match for operator[] (operand types are 'int [0]' and 'const
bitArray')|
The error is crystal clear that the operator[] expecting an interger type and what you passing a bitArray class type. Simple fix is to change it to integer.
However, here:
private:
int sortie[];
int n;
it is highly recommended to use std::vector, which gives a contiguous dynamic array whereas sortie[]is static allocation. Something like this:
See live here
#include <iostream>
#include <vector>
#include <cstddef>
class bitArray
{
private:
std::vector<int> sortie;
public:
explicit bitArray(int size): sortie(size) {}
bitArray& operator+=(const std::size_t i)
{
if (0 <= i && i < sortie.size()) // check for (0 <= index < size) of the array
{
this ->sortie[i] = 1;
return *this;
}
else
{
// do your logic! for instance, I have done something like follows:
std::cout << "out of bound" << std::endl;
if(sortie.size() == 0) sortie.resize(1,0); // if the size of array == 0
}
return *this;
}
int operator[] (const std::size_t index)
{
return (0 <= index && index < sortie.size()) ? sortie[index] : -1;
}
};
int main ()
{
bitArray obj(3);
obj += 0; std::cout << obj[0] << std::endl;
obj += -2; std::cout << obj[-2] << std::endl;
obj += 22; std::cout << obj[22] << std::endl;
return 0;
}
Update: Using C++17 feature std::optional, modified the above solution with the optional return type, which should be more readable.
See output in wandbox
#include <iostream>
#include <vector>
#include <cstddef>
#include <optional>
class bitArray
{
private:
std::vector<int> sortie;
public:
explicit bitArray(int size): sortie(size) {}
// optional is used as the return type
std::optional<bitArray> operator+=(const std::size_t i)
{
if (i < sortie.size()) // check for (0 <= index < size) of the array
{
this -> sortie[i] = 1;
return std::optional<bitArray>{*this};
}
std::cout << "out of bound operation+= \t";
return std::nullopt; // std::nullopt to create any (empty) std::optional
}
std::optional<int> operator[] (const std::size_t index)
{
if(index < sortie.size()) return std::optional<int>{sortie[index]};
else
{
std::cout << "out of bound operator[]: ";
return std::nullopt;
}
}
};
int main ()
{
bitArray obj(3);
obj += 0; std::cout << obj[0].value_or(-1) << std::endl;
obj += -2; std::cout << obj[-2].value_or(-1) << std::endl;
bitArray obj1(0);
obj1 += 22; std::cout << obj1[22].value_or(-1) << std::endl;
return 0;
}
Your operator+= takes a bitArray as parameter, but it should take the index where to set the 1 and thats basically what the error message is trying to tell you: There is no overload for the parameters you are trying to use it.
Note that to get such an operator you dont need to write your own array class, but you can provide an overload for std::vector:
#include <iostream>
#include <vector>
template <typename T>
std::vector<T>& operator+=(std::vector<T>& v,size_t index) {
v[index] = 1;
return v;
}
int main() {
auto vec = std::vector<int>(10,0);
vec += 5;
std::cout << vec[5];
return 0;
}
Note that this is a rather uncommon way to implement += (it does not really add anything). I would consider this as misuse of operator overloading and it will lead to obfuscated code. Consider this:
vec[5] = 1;
vs
vec += 5;
In the first line, everybody who is familiar with std::vector will know what it does, while for the second line 90% of the expectations will be off. I guess you are doing this as part of an assignment or homework, though for anything else I would suggest you to stay away from using operator overloads that do anything more than the obvious thing.

Is it possible to write one function for std::string and std::wstring?

I just wrote a simple utility function for std::string. Then I noticed that the function would look exactly the same if the std::string was a std::wstring or a std::u32string. Is it possible to use a template function here? I am not very familiar with templates, and std::string and std::wstring are templates themselves, which might be an issue.
template<class StdStringClass>
inline void removeOuterWhitespace(StdStringClass & strInOut)
{
const unsigned int uiBegin = strInOut.find_first_not_of(" \t\n");
if (uiBegin == StdStringClass::npos)
{
// the whole string is whitespace
strInOut.clear();
return;
}
const unsigned int uiEnd = strInOut.find_last_not_of(" \t\n");
strInOut = strInOut.substr(uiBegin, uiEnd - uiBegin + 1);
}
Is this a proper way to do it? Are there pitfalls with this idea. I am not talking about this function but the general concept of using a templated class StdStringClass and calling the usual std::string functions like find, replace, erase, etc.
Its a good Idea, But I'd build the template on top of std::basic_string rather then general StdStringclass
template<class T>
inline void removeOuterWhitespace(std::basic_string<T>& strInOut)
{
constexpr auto delim[] = {T(' '),T('\t'),T('\n'),T(0)};
const auto uiBegin = strInOut.find_first_not_of(delim);
if (uiBegin == std::basic_string<T>::npos)
{
// the whole string is whitespace
strInOut.clear();
return;
}
const auto uiEnd = strInOut.find_last_not_of(delim);
strInOut = strInOut.substr(uiBegin, uiEnd - uiBegin + 1);
}
I would also ditch the MSDN-style "inout" notation in favro for simpler name like str. programmer will guess themselves that str is the result since it is passed as non-const reference and function returns void.
also, I changed unsigned int to auto. all the standard C++ containers/strings return size_t when returning indexes. size_t might not be unsigned int. auto matches itself to the right return value.
Assuming your template works as expected (haven't checked...sorry), another option would be to wrap the function in class, and control which types of strings classes you'd like the function to be applied to using constructors.
EDIT: added illustrative framework
EDIT2 one that compiles (at least with vs2015) :-)
class StringType1;
class StringTypeN;
class str {
//template function
template<class StdStringClass>
inline void removeOuterWhitespace(StdStringClass & strInOut)
{
//.
//.
//.
}
public:
//constructors
str(StringType1 &s1) { removeOuterWhitespace(s1); }
//.
//.
//.
str(StringTypeN &sN) { removeOuterWhitespace(sN); }
};
int main() {
return 0;
}
EDIT3 Proof of concept
#include <iostream>
class incr {
//template function
template<class incrementor>
inline void removeOuterWhitespace(incrementor & n)
{
n++;
}
public:
//constructors
incr(int &n1) { removeOuterWhitespace(n1); }
incr(double &n1) { removeOuterWhitespace(n1); }
incr(float &n1) { removeOuterWhitespace(n1); }
};
int main() {
int n1 = 1;
double n2 = 2;
float n3 = 3;
std::cout << n1 << "\t" << n2 << "\t" << n3 << std::endl;
auto test1 = incr(n1);
auto test2 = incr(n2);
auto test3 = incr(n3);
//all variables modified
std::cout << "all variables modified by constructing incr" << std::endl;
std::cout << n1 << "\t" << n2 << "\t" << n3 << std::endl;
return 0;
}

Template function gives "no matching function for call" error

First question on stackoverflow :) I'm relatively new to C++, and have never used templates, so forgive me if I'm doing something silly. I have a template function that combs through a list and checks for a specified element of a general type. That way I can specify whether it's looking for a string, or an int, or whatever.
template <class T>
bool inList(T match, std::string list)
{
int listlen = sizeof(list);
for (int i = 0; i <= listlen; i++) {
if (list[i] == match) return true;
else continue;
}
return false;
};
This is my call to inList(). testvec is a string vector with a few elements, including "test":
if (inList<string>("test", testvec))
cout << "success!";
else cout << "fail :(";
To my dismay and confusion, upon compiling, I am slapped with the following error:
error: no matching function for call to 'inList(const char [5], std::vector<std::basic_string<char> >&)'
What am I doing incorrectly? :(
[EDIT]
I neglected to mention that the template definition is in the global namespace. (it's a simple test program to see if my template will work, and it does not, apparently :( )
That's because there is no way to convert from a std::vector to a std::string. Instead, what you need to do is abstract over the concept of a collection.
You do this so that people can pass in any collection type they want. They can use an array, a vector, a string, a list, a deque... as long as it fits the 'concept' of a collection (here's to hoping c++1x comes with concepts!). They can even use their own in-house specially optimized collection types. That's the beauty of templates.
Using C++11 (works with any standard collection, primitive arrays, and user-defined types):
template<class elem_t, class list_t>
bool in_list(const elem_t& elem, const list_t& list) {
for (const auto& i : list) {
if (elem == i) {
return true;
}
}
return false;
}
EDIT: It's a non-standard extension to deduce std::initializer_list as a template argument, so provide an explicit override:
template<class elem_t>
bool in_list(const elem_t& elem, std::initializer_list<elem_t> list) {
for (const auto& i : list) {
if (elem == i) {
return true;
}
}
return false;
}
With this version, you can call it like:
int main() {
std::vector<int> a = {1, 2, 3, 4, 5};
std::cout << in_list(3, a) << std::endl;
std::string b = "asdfg";
std::cout << in_list('d', b) << std::endl;
std::cout << in_list('d', "asdfg") << std::endl;
std::cout << in_list(3, {1, 2, 3, 4, 5}) << std::endl;
return 0;
}
And for those of us still in C++98, this will work for both strings and vectors, and some user-defined types. It will not work with raw arrays though.
template<class elem_t, class list_t>
bool in_list_98(const elem_t& elem, const list_t& list) {
list_t::const_iterator end = list.end(); //prevent recomputation of end each iteration
for (list_t::const_iterator i = list.begin(); i < end; ++i) {
if (elem == *i) {
return true;
}
}
return false;
}
Or, you can go STL style:
template<class elem_t, class iterator_t>
bool in_list_stl(const elem_t& elem, iterator_t begin, iterator_t end) {
for (iterator_t i = begin; i < end; ++i) {
if (elem == *i) {
return true;
}
}
return false;
}
//call like std::string s = "asdf"; in_list_stl('s', s.begin(), s.end());
If I made a mistake, sorry, I don't have my compiler running right now...

STL non-copying wrapper around an existing array?

Is it possible to create an STL-like container, or even just an STL-style iterator, for an existing array of POD-type elements?
For example, suppose I have an array of ints. It would be convenient to be able to call some of the STL functions, such as find_if, count_if, or sort directly on this array.
Non-solution: copying the entire array, or even just references to the elements. The goal is to be very memory- and time-saving while hopefully allowing use of other STL algorithms.
You can call many of the STL algorithms directly on a regular C style array - they were designed for this to work. e.g.,:
int ary[100];
// init ...
std::sort(ary, ary+100); // sorts the array
std::find(ary, ary+100, pred); find some element
I think you'll find that most stuff works just as you would expect.
You can use an inline function template so that you don't have to duplicate the array index
template <typename T, int I>
inline T * array_begin (T (&t)[I])
{
return t;
}
template <typename T, int I>
inline T * array_end (T (&t)[I])
{
return t + I;
}
void foo ()
{
int array[100];
std::find (array_begin (array)
, array_end (array)
, 10);
}
All the STL algorithms use iterators.
A pointer is a valid iterator into an array of objects.
N.B.The end iterator must be one element past the end of the array. Hence the data+5 in the following code.
#include <algorithm>
#include <iostream>
#include <iterator>
int main()
{
int data[] = {4,3,7,5,8};
std::sort(data,data+5);
std::copy(data,data+5,std::ostream_iterator<int>(std::cout,"\t"));
}
You can use Boost.Array to create a C++ array type with STL semantics.
using arrays:
int a[100];
for (int i = 0; i < 100; ++i)
a[i] = 0;
using boost.arrays:
boost::array<int,100> a;
for (boost::array<int,100>::iterator i = a.begin(); i != a.end(); ++i)
*i = 0;
Update: With C++11, you can now use std::array.
A pointer is a valid model of an iterator:
struct Bob
{ int val; };
bool operator<(const Bob& lhs, const Bob& rhs)
{ return lhs.val < rhs.val; }
// let's do a reverse sort
bool pred(const Bob& lhs, const Bob& rhs)
{ return lhs.val > rhs.val; }
bool isBobNumberTwo(const Bob& bob) { return bob.val == 2; }
int main()
{
Bob bobs[4]; // ok, so we have 4 bobs!
const size_t size = sizeof(bobs)/sizeof(Bob);
bobs[0].val = 1; bobs[1].val = 4; bobs[2].val = 2; bobs[3].val = 3;
// sort using std::less<Bob> wich uses operator <
std::sort(bobs, bobs + size);
std::cout << bobs[0].val << std::endl;
std::cout << bobs[1].val << std::endl;
std::cout << bobs[2].val << std::endl;
std::cout << bobs[3].val << std::endl;
// sort using pred
std::sort(bobs, bobs + size, pred);
std::cout << bobs[0].val << std::endl;
std::cout << bobs[1].val << std::endl;
std::cout << bobs[2].val << std::endl;
std::cout << bobs[3].val << std::endl;
//Let's find Bob number 2
Bob* bob = std::find_if(bobs, bobs + size, isBobNumberTwo);
if (bob->val == 2)
std::cout << "Ok, found the right one!\n";
else
std::cout << "Whoops!\n";
return 0;
}