Template functions in C++ for immutable arrays - c++

I'm trying to create a template function that will return true if a value from an immutable array exists, and false otherwise. Here is the code:
#include <algorithm>
using std::find;
#include <vector>
using std::vector;
template<class T>
bool contains(const T *arr, int size, T val) {
vector<T> dest(arr, arr + size);
T *p = find(dest.begin(), dest.end(), val);
if (p == arr + size)
return false;
else
return true;
}
Every time I compile against a simple test program I get these errors:
main.cpp:10: instantiated from 'void testit(const T*, int, T) [with T = char]'
main.cpp:37: instantiated from here
main.cpp:69: error: cannot convert '__gnu_cxx::__normal_iterator<char*, std::vector<char, std::allocator<char> > >' to 'char*' in initialization
main.cpp: In function 'bool contains(const T*, int, T) [with T = int]':
main.cpp:10: instantiated from 'void testit(const T*, int, T) [with T = int]'
main.cpp:43: instantiated from here
main.cpp:69: error: cannot convert '__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >' to 'int*' in initialization
main.cpp: In function 'bool contains(const T*, int, T) [with T = std::string]':
main.cpp:10: instantiated from 'void testit(const T*, int, T) [with T = std::string]'
main.cpp:44: instantiated from here
(All of those errors are on the line that contains the find function)
What is the best way to go about this? Is the vector even neccessary?

Is the vector even neccessary?
No.
template<class T>
bool contains(const T *arr, int size, T val) {
return std::find(arr, arr + size, val) != arr + size;
}

If the argument is not an actual, complete array T.C.'s answer's solid (though taking the value by reference is a good idea), but if you only want to support entire arrays you can have something a smidge easier to use, with the array size extracted from the array's type:
template <class T, size_t N>
inline bool contains(const T(&arr)[N], const T& val)
{
return std::find(arr, arr + N, val) != arr + N;
}
Of course you can have both versions overloaded.

error: cannot convert '__gnu_cxx::__normal_iterator > >' to 'char*' in initialization
This is telling you the return type you have is not correct for std::find.
For the code you have, you should have something like the following.
template<class T>
bool contains(const T *arr, int size, T val)
{
vector<T> dest(arr, arr + size);
vector<T>::iterator p = find(dest.begin(), dest.end(), val);
return p != dest.end();
}
Is the vector even neccessary?
No, it could be simplified as follows.
template<class T>
bool contains(const T *arr, int size, T val)
{
const T* end = arr + size;
return find(arr, end, val) != end;
}

Related

How can I create a vector based on a variable type

I'm using a template function, which the goal is reciever a vector and a function, and return the function type.
template <typename T, typename Function>
auto apply(const std::vector<T>& V, const Function &F){
vector<Function> x; # ERROR HERE
return x;
}
But the IDE give me error (http://coliru.stacked-crooked.com/a/ee6ce2127e013a18):
/usr/local/include/c++/10.2.0/ext/new_allocator.h: In instantiation of 'class __gnu_cxx::new_allocator<double(double)>':
/usr/local/include/c++/10.2.0/bits/allocator.h:116:11: required from 'class std::allocator<double(double)>'
/usr/local/include/c++/10.2.0/bits/stl_vector.h:87:21: required from 'struct std::_Vector_base<double(double), std::allocator<double(double)> >'
/usr/local/include/c++/10.2.0/bits/stl_vector.h:389:11: required from 'class std::vector<double(double), std::allocator<double(double)> >'
main.cpp:10:22: required from 'auto apply(const std::vector<T>&, const Function&) [with T = int; Function = double(double)]'
main.cpp:19:39: required from here
/usr/local/include/c++/10.2.0/ext/new_allocator.h:96:7: error: 'const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = double(double); __gnu_cxx::new_allocator<_Tp>::const_pointer = double (*)(double); __gnu_cxx::new_allocator<_Tp>::const_reference = double (&)(double)]' cannot be overloaded with '_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = double(double); __gnu_cxx::new_allocator<_Tp>::pointer = double (*)(double); __gnu_cxx::new_allocator<_Tp>::reference = double (&)(double)]'
96 | address(const_reference __x) const _GLIBCXX_NOEXCEPT
| ^~~~~~~
/usr/local/include/c++/10.2.0/ext/new_allocator.h:92:7: note: previous declaration '_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = double(double); __gnu_cxx::new_allocator<_Tp>::pointer = double (*)(double); __gnu_cxx::new_allocator<_Tp>::reference = double (&)(double)]'
92 | address(reference __x) const _GLIBCXX_NOEXCEPT
| ^~~~~~~
main.cpp: In function 'int main(int, char**)':
main.cpp:19:31: error: conversion from 'vector<double(double),allocator<double(double)>>' to non-scalar type 'vector<double,allocator<double>>' requested
19 | vector<double> r = ::apply(v, seno);
| ~~~~~~~^~~~~~~~~
This is call of the main function.
double seno( double n ) { return sin(n); }
int main( int argc, char* argv[]) {
vector<int> v{ 1, 2, 3, 4, 5 };
vector<double> r = ::apply(v, seno);
cout << r;
return 0;
}
I don't know what I'm doing wrong, so How can I improve this method and pass trough this error?
EDIT: The purpse to generalize the in method insted of using double in the vector is because I want o re-use in another way. So I've generalize the most that I can.
vector<Function> x; // ERROR HERE defines a vector of function pointers. But that's not what you want - you want a vector of the return type of the function. And that's what decltype() is for.
In your apply function, F is the function to be called and T is the type of the values in the vector being passed in. That means T() is the default value of the items in the vector (in this case the default value of int is 0). Then, F(T()) would actually call the function with 0 and return something so decltype(F(T())) tells you the type of the thing returned.
That means you need to write vector<decltype(F(T()))> x; instead.
T() works because the type is int and it is default constructible. As #alterigel said in the comments std::declval<T>() is better when the type is not default constructible.
So vector<decltype(F(std::declval<T>()))> x; might be needed in some situations.
The whole program would look like:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
template <typename T, typename Function>
auto apply(const std::vector<T>& V, const Function &F) {
vector<decltype(F(T()))> x;
for(auto a : V)
x.push_back(F(a));
return x;
}
double seno( double n ) { return sin(n); }
int main( int argc, char* argv[]) {
vector<int> v{ 1, 2, 3, 4, 5 };
vector<double> r = ::apply(v, seno);
for (auto a : r)
cout << a << " ";
return 0;
}
Try it here: https://onlinegdb.com/SknTsVaHO

Failure to create C++ arrays recursively

I want to test a recursion method and the following code has a compilation error. I've double checked the subscripts variables, and they should be correct. I really couldn't figure it out, can somebody help me?
#include <iostream>
#include <array>
using namespace std;
template <typename T, size_t Size>
void fn(array<T, Size>& data) {
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
if (data.size() > 1 ) {
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
fn(right);
}
}
int main() {
array<int, 5> test;
fn(test);
}
The compilation error is:
$ g++ -std=c++14 GuessNumber.cpp -o test
In file included from GuessNumber.cpp:2:0:
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/include/c++/array: In instantiation of ‘struct std::array<int, 2305843009213693952>’:
GuessNumber.cpp:15:9: recursively required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 2]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 5]’
GuessNumber.cpp:21:11: required from here
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/include/c++/array:94:12: error: size of type ‘std::array<int, 2305843009213693952>’ is too large (‘9223372036854775808’ bytes)
struct array
^~~~~
First of all, let us understand where the problem comes from.
The error you get
/usr/lib/gcc/x86_64-pc-cygwin/7.3.0/include/c++/array: In instantiation of ‘struct std::array<int, 2305843009213693952>’:
GuessNumber.cpp:15:9: recursively required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 2]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 5]’
tells you that you first instantiate fn with Size=5 (because you call it in the main), and this recursively instantiates fn with Size=2. Unfortunately the compiler does not show the full recursion, otherwise you would see that the recursion does not end here.
If you use in the program an array of size 2
array<int, 2> test;
you will see an additional level of recursion, in the error message:
GuessNumber.cpp:15:9: recursively required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 1]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 2]’
GuessNumber.cpp:21:11: required from here
which again, tells you that the instantiation of fn with Size=2 triggers the intantation of fn with Size=1. But the recursion does not end here. Try with
array<int, 1> test;
and you will finally see what is happpening:
/usr/include/c++/10.1.0/array: In instantiation of ‘struct std::__array_traits<int, 9223372036854775808>’:
/usr/include/c++/10.1.0/array:110:56: required from ‘struct std::array<int, 9223372036854775808>’
GuessNumber.cpp:13:33: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 0]’
GuessNumber.cpp:15:9: required from ‘void fn(std::array<_Tp, _Nm>&) [with T = int; long unsigned int Size = 1]’
GuessNumber.cpp:21:11: required from here
When Size=1, the compiler fully generates fn, including the code in the braces if (data.size() > 1). Even if this condition is always false, still the code is parsed and compiled. This means that, inside the never-executed-code, you instantiate fn with Size=0. But then, you have an overflow in the variable end, which attains a large value. Then the code after if instantiates an std::array with the exceedingly large size.
To fix this, you need to stop the compiler from generating code when Size=0.
This can be done in several ways.
With c++17 you have a very convenient if constexpr. If the condition of if constexpr is not true, the code is not instatiated at all, ending then the template recursion. So you can substitute
if (data.size() > 1 ) {
with
if constexpr (Size > 1 ) {
And compile with std=c++17. Notice that I changed the condition to Size > 1 because the data variable is not constexpr, hence you cannot use it at compile time.
If you do not have c++17, you can use SFINAE instead.
#include <iostream>
#include <array>
#include <type_traits>
using namespace std;
template <typename T, size_t Size>
typename std::enable_if<(Size == 0)>::type fn(array<T, Size>& data) { }
template <typename T, size_t Size>
typename std::enable_if<(Size > 0)>::type fn(array<T, Size>& data) {
const size_t begin{0};
const size_t end{Size-1}; // 1
const size_t leftUpper{(begin+end)/2}; // 0
const size_t rightLower{leftUpper+1}; // 1
if (data.size() > 1 ) {
array<T, end+1-rightLower> right; // 1
cout << "Right: " << end+1-rightLower << endl;
fn(right);
}
}
int main() {
array<int, 5> test;
fn(test);
}
The two approaches are perfectly equivalent, see here.
Try this:
#include <iostream>
#include <array>
using namespace std;
// the function contains its body just because looks like
// you want to implement some other logic there
template <typename T>
void fn(array<T, 2ul>& data) {
const size_t Size = 2;
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
}
template <typename T>
void fn(array<T, 1ul>& data) {
}
template <typename T>
void fn(array<T, 1ul>& data) {
const size_t Size = 1;
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
}
template <typename T, size_t Size>
void fn(array<T, Size>& data) {
const size_t begin{0};
const size_t end{Size-1};
const size_t leftUpper{(begin+end)/2};
const size_t rightLower{leftUpper+1};
if (data.size() > 1 ) {
array<T, end+1-rightLower> right;
cout << "Right: " << end+1-rightLower << endl;
fn(right);
}
}
int main() {
array<int, 5> test;
fn(test);
}
You code is not compiled conditionally. ifs do not work like you expect when you do template magic. One more example is here

Converting C++ method argument to template argument fails with compile error

I'm refactoring an API I've made a couple of years ago bringing a polymorphic implementation of common data structures (lists, maps, sets, trees, graphs), available at http://www.data-types.com. For structures requiring values comparison (eg: hash table), I've made them use C comparators and everything goes well.
In next version, however, I'm trying to level comparators' behavior so that users won't be required to create one when it can be inferred already. Everything is going fine until I try to convert a method argument into a template argument. Example code reduced to bare bone:
#include <iostream>
#include <cstddef>
#include <stdexcept>
template<typename VALUE>
class HashTable {
public:
HashTable(int (*comparator)(const VALUE&,const VALUE&), std::size_t (*hasher)(const VALUE&)){
std::cout << "HashTable constructor called" << std::endl;
}
bool contains(const VALUE& value, int (*customCompare)(const VALUE&,const VALUE&)) const{
return false;
}
~HashTable(){
std::cout << "HashTable destructor called" << std::endl;
}
};
template<typename _KEY, typename _VALUE>
struct MapEntry {
_KEY key;
_VALUE value;
};
template<typename KEY, typename VALUE, int (*comparator)(const KEY&, const KEY&)>
static inline int compareMapKey(const MapEntry<KEY,VALUE>& left, const MapEntry<KEY,VALUE>& right) {
return comparator(left.key, right.key);
}
template<typename KEY, typename VALUE, int (*comparator)(const VALUE&, const VALUE&)>
static inline int compareMapValue(const MapEntry<KEY,VALUE>& left, const MapEntry<KEY,VALUE>& right) {
return comparator(left.value, right.value);
}
template<typename KEY, typename VALUE, std::size_t (*hash)(const KEY&)>
static inline std::size_t hashMapKey(const MapEntry<KEY, VALUE>& element) {
return hash(element.key);
}
template<typename KEY, typename VALUE, int (*compareByKey)(const KEY&, const KEY&), std::size_t (*hashByKey)(const KEY&)>
class HashMap {
public:
HashMap(){
hashTable = new HashTable<MapEntry<KEY,VALUE>>(compareMapKey<KEY, VALUE, compareByKey>, hashMapKey<KEY, VALUE, hashByKey>);
std::cout << "HashMap constructor called" << std::endl;
}
bool containsValue(const VALUE& value, int (*comparator)(const VALUE&, const VALUE&)) const{
MapEntry<KEY,VALUE> mapEntry;
mapEntry.value = value;
return hashTable->contains(mapEntry, compareMapValue<KEY, VALUE, comparator>);
}
~HashMap(){
delete hashTable;
std::cout << "HashMap destructor called" << std::endl;
}
private:
HashTable<MapEntry<KEY,VALUE>>* hashTable;
};
static inline int comparator(const long& left, const long& right) {
if(left<right) return -1;
else if (left>right) return 1;
else return 0;
}
static inline std::size_t hash(const long& item) {
return item;
}
int main() {
long val = 1;
HashMap<long, long, comparator, hash> map;
map.containsValue(val, comparator);
std::cout << "!!!Hello World!!!" << std::endl;
return 0;
}
This refuses to compile, giving me this error:
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/Test.d" -MT"src/Test.o" -o "src/Test.o" "../src/Test.cpp"
../src/Test.cpp: In instantiation of ‘bool HashMap<KEY, VALUE, compareByKey, hashByKey>::containsValue(const VALUE&, int (*)(const VALUE&, const VALUE&)) const [with KEY = long int; VALUE = long int; int (* compareByKey)(const KEY&, const KEY&) = comparator; std::size_t (* hashByKey)(const KEY&) = hash]’:
../src/Test.cpp:78:35: required from here
../src/Test.cpp:53:79: error: no matching function for call to ‘HashTable<MapEntry<long int, long int> >::contains(MapEntry<long int, long int>&, <unresolved overloaded function type>)’ return hashTable->contains(mapEntry, compareMapValue<KEY, VALUE, comparator>);
../src/Test.cpp:12:7: note: candidate: bool HashTable<VALUE>::contains(const VALUE&, int (*)(const VALUE&, const VALUE&)) const [with VALUE = MapEntry<long int, long int>] bool contains(const VALUE& value, int (*customCompare)(const VALUE&,const VALUE&)) const{
../src/Test.cpp:12:7: note: no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘int (*)(const MapEntry<long int, long int>&, const MapEntry<long int, long int>&)’
Does anyone know for a solution of above? My C++ skills have become rustier since I'm no longer habitually working in that language...
A fundamental property of C++ templates, is that all template parameters must be resolved at compile time. That is, every template parameter must be known at compile time. The line with the compilation error:
return hashTable->contains(mapEntry,
compareMapValue<KEY, VALUE, comparator>);
But this comparator is a parameter to this function:
bool containsValue(const VALUE& value,
int (*comparator)(const VALUE&, const VALUE&);
Therefore, generally speaking: what this "comparator` is, is not known at compile time. The actual value gets passed in at runtime. This is the fundamental reason for your compilation error: all template parameters must be specified at compile time. Simply put, this overall approach that's used here simply won't work.

error in type conversion in operator overloading

I have a template class and I need to overload operator ==. I do this in the following way
template <typename T>
class Polynomial {
vector<T> coefficients;
public:
Polynomial(vector<T> c);
bool operator ==(const Polynomial& second) const {
const typename vector<T>::iterator thisBegin = this->coefficients.begin();
const typename vector<T>::iterator secondBegin = second.coefficients.begin();
for ( ; ((thisBegin != this->coefficients.end()) &&
(secondBegin != second.coefficients.end()));
++thisBegin, ++secondBegin) {
if (*thisBegin != *secondBegin)
return false;
}
while (thisBegin != this->coefficients.end()) {
if (*thisBegin != 0)
return false;
++thisBegin;
}
while (secondBegin != second.coefficients.end()) {
if (*secondBegin != 0)
return false;
++secondBegin;
}
return true;
}
};
However, when I create two objects of this class with T=int and try to apply this operator
Polynomial<int> first(firstVector);
Polynomial<int> second(secondVector);
std::cout << (first == second) << std::endl;
I got the error
problem2.cpp: In instantiation of ‘bool Polynomial<T>::operator==(const Polynomial<T>&) const [with T = int; Polynomial<T> = Polynomial<int>]’:
problem2.cpp:63:32: required from here
problem2.cpp:23:83: error: conversion from ‘std::vector<int, std::allocator<int> >::const_iterator {aka __gnu_cxx::__normal_iterator<const int*, std::vector<int, std::allocator<int> > >}’ to non-scalar type ‘std::vector<int, std::allocator<int> >::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >}’ requested
Can someone point out what's wrong with this conversion? Thanks!
You are trying to convert a const_iterator to an iterator:
const typename vector<T>::iterator thisBegin = this->coefficients.begin();
this is const in this context, so this->coefficients.begin(); returns a const_iterator. Try this:
typename vector<T>::const_iterator thisBegin = this->coefficients.begin();
Note also that thisBegin is not const, as in your example. This is because you then do this kind of thing:
++secondBegin;
which requires the const_iterator to be non-const (meaning you can modify the iterator, but not the thing it points to).
your method is const that mean that you can only call const functions on this and
you passing const reference to method, so you can only call const functions on it
So, both
this->coefficients.begin();
second.coefficients.begin()
returns const iterators.
You cannot assign them to non-const ones.
There is a solution:
vector<T>::const_iterator& thisBegin = this->coefficients.begin();
vector<T>::const_iterator& secondBegin = second.coefficients.begin();
(use references to const_iterator)
Even better:
auto& thisBegin = this->coefficients.begin();
auto& secondBegin = second.coefficients.begin();
(use references to auto, C++11 feature)
BTW, you can simply compare two vectors using std::mismatch

Custom allocator works with vector but not with deque (wrapped within a queue)

I have written a custom allocator that allocates memory in a shared memory segment.
This line of code compiles (and runs) fine:
shp_arr = new (vecmem) vector<shape *,smallocator <shape*> > ;
But this line of code:
shp_queue = new (queuemem) queue< shape *, deque < shape *, smallocator< shape * > > > ;
gives me a number of errors. Here they are:
/usr/local/lib/gcc/i686-pc-cygwin/4.7.3/../../../../include/c++/4.7.3/bits/stl_deque.h: In
instantiation of ‘std::_Deque_base<_Tp, _Alloc>::_Map_alloc_type std::_Deque_base<_Tp,
_Alloc>::_M_get_map_allocator() const [with _Tp = shape*; _Alloc = smallocator<shape*>;
std::_Deque_base<_Tp, _Alloc>::_Map_alloc_type = smallocator<shape**>]’:
/usr/local/lib/gcc/i686-pc-cygwin/4.7.3/../../../../include/c++/4.7.3/bits/stl_deque.h:549:9:
required from ‘void std::_Deque_base<_Tp, _Alloc>::_M_deallocate_map(_Tp**, std::size_t) [with _Tp =
shape*; _Alloc = smallocator<shape*>; std::size_t = unsigned int]’
/usr/local/lib/gcc/i686-pc-cygwin/4.7.3/../../../../include/c++/4.7.3/bits/stl_deque.h:568:4:
required from ‘std::_Deque_base<_Tp, _Alloc>::~_Deque_base() [with _Tp = shape*; _Alloc =
smallocator<shape*>]’
/usr/local/lib/gcc/i686-pc-cygwin/4.7.3/../../../../include/c++/4.7.3/bits/stl_deque.h:781:15:
required from ‘std::deque<_Tp, _Alloc>::deque() [with _Tp = shape*; _Alloc = smallocator<shape*>]’
file.cpp:233:30: required from here
/usr/local/lib/gcc/i686-pc-cygwin/4.7.3/../../../../include/c++/4.7.3/bits/stl_deque.h:529:53: error:
no matching function for call to ‘smallocator<shape**>::smallocator(const _Tp_alloc_type&)’
/usr/local/lib/gcc/i686-pc-cygwin/4.7.3/../../../../include/c++/4.7.3/bits/stl_deque.h:529:53: note:
candidates are:
In file included from file.cpp:20:0:
smallocator.hpp:41:3: note: smallocator<T>::smallocator(const smallocator<T>&) [with T = shape**;
smallocator<T> = smallocator<shape**>]
smallocator.hpp:41:3: note: no known conversion for argument 1 from ‘const _Tp_alloc_type {aka const
smallocator<shape*>}’ to ‘const smallocator<shape**>&’
smallocator.hpp:40:3: note: smallocator<T>::smallocator() [with T = shape**]
smallocator.hpp:40:3: note: candidate expects 0 arguments, 1 provided
The interface to smallocator looks like this:
template <typename T>
class smallocator: public std::allocator<T>
{
public:
typedef size_t size_type;
typedef T* pointer;
typedef const T* const_pointer;
template<typename _Tp1>
struct rebind
{
typedef smallocator<_Tp1> other;
};
pointer allocate(size_type n, const void *hint=0)
{
...
}
void deallocate(pointer p, size_type n)
{
...
}
smallocator() throw(): std::allocator<T>() { std::cout <<"Hello allocator" <<std::endl;}
smallocator(const smallocator &a) throw(): std::allocator<T>(a) { }
~smallocator() throw() { }
};
Does anyone know what the problem is? Thanks!
You did not provide the following constructor for your smallocator :
template <class U>
smallocator(const smallocator<U>& a) throw();
You need all three :
smallocator() throw();
smallocator(const smallocator& a) throw();
template <class U>
smallocator(const smallocator<U>& a) throw();