Why does typedef affect function overloads? - c++

I have two functions in a class (please comment on the issue and not the coding style):
template <typename T>
class myStringClass
{
public:
...
typedef T* iterator;
void erase(size_t pos, size_t n); // FUNC#1
void erase(iterator first, iterator last); //FUNC#2
};
FUNC#2 is erasing the range while FUNC#1 simply calls FUNC#2 after calculating the appropriate range. In FUNC#1 instead of declaring iterator to calculate the range, I declared T* which is (should be?) essentially the same thing.
// SEGMENT#1 in function erase(size_t pos, size_t n)
T* begin = m_begin + pos;
T* end = begin + n;
erase(begin, end); // call FUNC#2
However, this does not compile. The compiler complains that it cannot convert T* (where T is a char) to size_t (i.e. trying to call `FUNC#1). But if I change the above code to:
// SEGMENT#2 in function erase(size_t pos, size_t n)
iterator begin = m_begin + pos;
iterator end = begin + n;
erase(begin, end); // call FUNC#2
Then the compiler is happy. I assumed that typedef was an alias and was not type-checked. So SEGMENT#1 == SEGMENT#1 as far as the compiler is concerned? Why does one compile and the other doesn't?
EDIT: After testing Oli's code, I checked it against mine and I forgot to add const to the iterators in SEGMENT#2. Aside from the argument that adding const does not make sense in this case, why does that produce the error for T* and not iterator. Here is Oli's code slightly modified if you want to give it a try:
#include <stdlib.h>
template <typename T>
class myStringClass
{
private:
T *m_begin;
public:
typedef T* iterator;
void erase(size_t pos, size_t n); // FUNC#1
void erase(iterator first, iterator last); //FUNC#2
};
template <typename T>
void myStringClass<T>::erase(size_t pos, size_t n)
{
const T* begin = m_begin + pos; // replace with iterator to compile
const T* end = begin + n; // replace with iterator to compile
erase(begin, end); // call the overload
}
template <typename T>
void myStringClass<T>::erase(const iterator first, const iterator last)
{
}
int main(void)
{
myStringClass<char> x;
x.erase(1,1);
}

The following code compiles fine:
#include <stdlib.h>
template <typename T>
class myStringClass
{
private:
T *m_begin;
public:
typedef T* iterator;
void erase(size_t pos, size_t n); // FUNC#1
void erase(iterator first, iterator last); //FUNC#2
};
template <typename T>
void myStringClass<T>::erase(size_t pos, size_t n)
{
T* begin = m_begin + pos;
T* end = begin + n;
erase(begin, end); // call the overload
}
template <typename T>
void myStringClass<T>::erase(iterator first, iterator last)
{
}
int main(void)
{
myStringClass<char> x;
x.erase(1,1);
}
Your problem must be elsewhere.
UPDATE
Now you've shown your real code...
The problem is you're trying to call a function that takes non-const pointers by passing it const pointers. This isn't valid.
UPDATE 2
Now that you've shown your "real real" code...
The problem is that this:
typedef T *U;
const U x;
is not the same as:
const T *x;
it's actually the same as:
T *const x;

It produces the error for const T * and not for const iterator. And the reason is that const iterator expands to T * const, not const T *.
extern int foo(int i);
extern int bar(int *i);
void baz()
{
const int x = 5;
int y = x;
foo(x); // Perfectly fine
foo(y); // Also perfectly fine
bar(&x); // Not fine at all.
bar(&y); // Perfectly fine.
}
void bouncy()
{
typedef int my_t;
typedef int *myptr_t;
typedef const my_t const_my_t; // const (int) aka const int
typedef const myptr_t const_myptr_t; // const (int *) aka int * const
}

Related

C++ Vector implementation allocating new objects

I'm almost done with the implementation of a std::vector type of vector (hopefully) but I'm having a little bug in the code and I can't seem to find where. Basically when I build Vector and use push_back, the vector automatically allocates new memory for more elements (specifically twice the size of the vector) but when doing so, the "extra space" is initialized to 0's and I don't know why.
Here is my code:
Vector.h
#include <memory>
#include <cstddef>
template <class T>
class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec(){create();}
Vec(size_type n, const T& val = T()) {create(n, val);}
~Vec() {uncreate();}
//copy constructor
Vec(const Vec& v) {create(v.begin(), v.end());}
//assignment operator
Vec& operator=(const Vec&);
size_type size() const {return limit - data;}
//index operators
T& operator[](size_type i) {return data[i];}
const T& operator[](size_type i) const {return data[i];}
iterator begin() {return data;}
const_iterator begin() const {return data;}
iterator end() {return limit;}
const_iterator end() const {return limit;}
void push_back(const T&);
private:
iterator data; //1st element
iterator avail; //one past last constructed element
iterator limit; //one past last available element
//Memory management
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
template <class T>
void Vec<T>::push_back(const T& val){
if(avail == limit)
grow();
unchecked_append(val);
}
template <class T>
Vec<T>& Vec<T>::operator=(const Vec& rhs){
//self-assign
if(&rhs != this){
uncreate;
create(rhs.begin(), rhs.end());
}
return *this;
}
// Empty Vector, pointers to 0
template <class T> void Vec<T>::create(){
data = avail = limit = 0;
}
// Allocate memory for (size)
template <class T> void Vec<T>::create(size_type n, const T& val){
data = alloc.allocate(n); // returns pointer to first element
limit = avail = data +n;
std::uninitialized_fill(data, limit, val);
}
template <class T> void Vec<T>::create(const_iterator i, const_iterator j){
data = alloc.allocate(j-i);
limit = avail =std::uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate(){
if(data){
iterator it = avail;
while(it != data)
alloc.destroy(--it);
// Free space
alloc.deallocate(data, limit - data);
}
// Empty Vector
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow(){
// Allocate twice the space we had
size_type new_size = std::max(2 * (limit - data), ptrdiff_t(1));
// Allocate new space and copy to new space
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
// Return old space used
uncreate();
// Reset pointers to point to new space
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template <class T> void Vec<T>::unchecked_append(const T& val){
alloc.construct(avail++, val);
}
And this is how I created the vector
main.cpp
#include "vector.h"
#incude <iostream>
using namespace std;
int main(){
Vec<int> v1;
v1.push_back(12);
v1.push_back(9);
v1.push_back(74);
v1.push_back(22);
Vec<int> v2 = v1;
v2.push_back(70); //After doing this the vector is (12, 9, 74, 22, 70, 0, 0, 0)
for(auto e: v2) cout << e << " ";
cout << endl;
}
Thanks for any help and also any feedback on the actual code is appreciated. :)
The problem is (i do not know if it is really problem)
template <class T> void Vec<T>::grow(){
// Allocate twice the space we had
size_type new_size = std::max(2 * (limit - data), ptrdiff_t(1));
....
....
}
No need to do any vector copy to reproduce your problem.
The problem can be seen even in first vector after inserting 5th elemnt
v1.push_back(12);
v1.push_back(9);
v1.push_back(74);
v1.push_back(22);
v1.push_back(22); //5th elemnt.
now printing the size;
cout<<v1.size()<<endl; - it says 8.
Even the std vector, after resize(), it is normal behavior to print zeros for the empty elements.
Look at this example:
http://en.cppreference.com/w/cpp/container/vector/resize
if you do not want additional zeros, you have to tweak your ":new_size" calculation.

Autoconverting template< T> to template<const T>

This piece below is supposed to be primarily for a string view with T={char, const char} being the primary intended template instantiation target.
The cmp function is supposed to compare the views analogously to strcmp.
The problem is that while char* happily converts to const char* I don't know how to get SVec<char> to convert to SVec<const char> just as happily.
The last line (cout<<(cmp(rv, rvc));) won't compile. I have to do the convertion explicitly (cmp(SVec<const char>(rv), rvc)). Can it be automatic like with char* to const char*?
The code (much simplified):
template <typename T>
class SVec {
protected:
T* begin_;
size_t size_;
public:
SVec(T* begin, size_t size) : begin_(begin), size_(size) {};
SVec(T* begin, T* end) : begin_(begin), size_(end-begin) {};
SVec(T* begin) : begin_(begin) { while (*(begin++)) {}; size_ = begin - 1 - begin_; }
//^null element indicates the end
///Conversion
operator SVec<const T>() const { return SVec<const T>(begin_, size_); }
};
//General lexicographic compare
template <typename T>
inline int cmp(const SVec<const T>& l, const SVec<const T> & r){
return 1;
}
//Char specialization
template <> inline int cmp<char>(const SVec<const char>& l, const SVec<const char>& r){
return 1;
}
//Explicit instantiation
template int cmp<char>(const SVec<const char>& l, const SVec<const char>& r);
#include <iostream>
int main(){
using namespace std;
char ar[] = "st";
SVec<char> sv = ar;
SVec<const char> svc = "str";
cout<<(cmp(SVec<const char>(sv), svc));
cout<<(cmp(sv, svc));
}
So the first thing you should probably do is make cmp a Koenig operator.
Then we can tag dispatch between the char and non-char versions:
template <typename T>
class SVec {
private:
static T* find_end(T* in) {
// I think while(*in)++in; would be better
// then the end is the null, not one-past-the-null.
while(*in++) {};
return in;
}
protected:
T* begin_ = nullptr;
size_t size_ = 0;
public:
SVec() = default;
SVec(SVec const&) = default;
SVec(T* begin, size_t size) : begin_(begin), size_(size) {};
SVec(T* begin, T* end) : SVec(begin, end-begin) {}
SVec(T* begin) : SVec(begin, find_end(begin)) {}
operator SVec<const T>() const { return SVec<const T>(begin_, size_); }
friend int cmp(SVec<T> l, SVec<T> r) {
return cmp_impl(l, r, std::is_same<std::decay_t<T>,char>{});
}
private:
static int cmp_impl(SVec<const char> l, SVec<const char> r, std::true_type){
return 1;
}
static int cmp_impl(SVec<const T> l, SVec<const T> r, std::false_type){
return 1;
}
};
std::decay_t and enable_if_t are C++14, but are just short versions of the typename spam _t-less versions.
Notice I take things by value instead of const& : a pointer and a size_t do not merit passing by reference.
I also forward all ctors into 2 bottlenecks.
...
The Koenig operator friend int cmp uses ADL to be found. It is not a template function, but rather a function that is generated for each template class instance, which is an important distinction.
Koenig operators avoid the problems of template operators, while allowing them to vary with the type of the template. Such an operator can only be found via ADL (argument dependent lookup).
It then dispatches to the _impl overloads (which are now const-correct) based on if T is a char or not at compile time.

Fixed Allocator with stack - how to implement allocate and deallocate?

I've started to code FixedAllocator class that allocates memory by chunks of fixed size and works as stack, so that it works in constant time to allocate/deallocate. Actually, I'll need this class to use it with std::vector, so that I have to implement all std::allocator methods.
Everything here is for learning purposes so that - I don't need any complete implementations or headers - the real ones have a lot of code over my problem.
And I got stuck on allocate/deallocate methods - I understand that I should somehow reserve some memory pool - for example using vector, I understand that I should use static_cast to convert char type into T-type, but I don't completely understand how to rebuild this two ideas into list. Deallocate takes pointer as argument, not TNode - that's maybe the main problem.
If someone already wrote this kind of allocator - answer with code will be perfect.
Any suggestions, links and other source of knowledge are welcome. Thank you.
Here is the skeleton of code:
template <typename T, unsigned int nodeSize>
class FixedAllocator : public std::allocator<T>{
private:
static size_t Used;
static const size_t MAX_SIZE = 100000;
struct TNode {
TNode* next;
char data[nodeSize];
};
TNode* head;
public:
typedef T* pointer;
typedef const T* const_pointer;
typedef T & reference;
typedef const T & const_reference;
typedef T value_type;
template <typename U> struct rebind { typedef allocator<U> other; };
FixedAllocator() {
if (Pool.empty()) {
Pool.resize(MAX_SIZE * sizeof(T));
Used = 0;
}
}
FixedAllocator(const FixedAllocator &) {
}
template<typename U>
FixedAllocator(const FixedAllocator<U> &) {
if (Pool.empty()) {
Pool.resize(MAX_SIZE * sizeof(T));
Used = 0;
}
}
pointer address(reference x) const {
return &x;
}
const_pointer address(const_reference x) const {
return &x;
}
pointer allocate(size_t n, FixedAllocator<void>::const_pointer = 0) {}
void deallocate(pointer, size_t) {}
size_t max_size() const throw() {
return MAX_SIZE - size;
}
void construct(pointer p, const_reference val) {
new (static_cast<void*>(p)) value_type(val);
}
void destroy(pointer p) {
p->~value_type();
}
};

cannot find constructor in C++ templated code [duplicate]

This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 9 years ago.
I am getting this error when compiling it with: g++ main.cpp Vec.cpp -Wall -o main -I.
/tmp/cciqbEQJ.o: In function `main':
main.cpp:(.text+0x8b): undefined reference to `Vec<double>::Vec()'
main.cpp:(.text+0x9b): undefined reference to `Vec<double>::~Vec()'
collect2: ld returned 1 exit status
make: *** [all] Error 1
I don't understand because I have done a lot of multiple-source file programs before and I never got this error where the constructor is not found. It seems like the compiler is not able to dynamically bind the template code to the instantiation of the template. Also, I have put a macro guard on the .h file but it is not shown below.
The source codes are below:
Vec.cpp
#include "Vec.h"
using namespace std;
template<class T>
Vec<T>::Vec() {
create();
}
template<class T>
Vec<T>::Vec( size_type n, const value_type& t ){
create(n,t);
}
template<class T>
Vec<T>::Vec(const Vec& v)
{
create(v.begin(), v.end());
}
template<class T>
Vec<T>::~Vec(){
uncreate();
}
template<class T>
void Vec<T>::create()
{
data = avail = limit = 0;
}
template<class T>
void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data,limit, val);
}
template<class T>
void Vec<T>::create(const_iterator i, const_iterator j) {
data = alloc.allocate(j-i);
limit = avail = uninitialized_copy(i, j, data);
}
template<class T>
void Vec<T>::uncreate() {
if (data) {
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data,limit-data);
}
data = limit = avail =0;
}
template<class T> void Vec<T>::grow() {
size_type new_size = max ( 2 * (limit-data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = unitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template<class T> void Vec<T>::unchecked_append(const T& val) {
alloc.construct(avail++, val);
}
template<class T>
void Vec<T>::push_back(const T& t){
if ( avail == limit )
grow();
unchecked_append(t);
}
Vec.h
template<class T> class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec();
Vec( size_type n, const T& t=T() );
Vec(const Vec& v);
Vec& operator=(const Vec& v);
~Vec();
void push_back(const T& t);
inline size_type size() const { return limit - data; }
inline iterator begin() {return data;}
inline const_iterator begin() const { return data; }
inline iterator end() { return limit; }
inline const_iterator end() const { return limit; }
inline T& operator[](size_type i){
return data[i];
}
const T& operator[](size_type i) const { return data[i]; }
private:
iterator data;
iterator limit;
iterator avail;
//facilities for memory allocation
allocator<T> alloc;
//allocate and initialize the underlying array
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
//destroy the elements in the array and free the memory
void uncreate();
//support functions for push_back
void grow();
void unchecked_append(const T&);
};
main.cpp
int main(void) {
Vec<double> test;
}
In order for the compiler to generate the code, it must see both the template definition and the specific types used to for the template.
So, in main.cpp add line just #include "Vec.cpp" at the top.
Compile using
g++ main.cpp -Wall -o main -I. <-- Notice Vec.cpp removed now.
This will make sure the template declaration and implementation are together during compilation, at same time implementation is still separated from declaration.
Another alternative is to include this .cpp while at the end of Vec.h as suggested in the above commented link of SO by juanchopanza
For more details Ref :- Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file ?

Implementing a simplified C++ vector class copy - won't compile

Chapter 11 of Accelerated C++ covers implementing template classes, using a simplified version of the STL's vector class as an example. Exercise 11-6 wants us to add the .erase() and .clear() methods to the class, so first I copied the final code directly from the book and tried to compile, but it failed. I then moved all the function definitions into the .h file (removing the Vec<T>:: etc. stuff as necessary) and compiled just my main.cpp, which worked.
Here's all of my code:
main.cpp
#include <iostream>
#include "Vec.h"
using std::cout;
using std::endl;
int main()
{
Vec<int> v;
for (int i = 1; i < 10; ++i)
v.push_back(i);
for(Vec<int>::const_iterator iter = v.begin();
iter != v.end(); ++iter)
cout << *iter << endl;
return 0;
}
Vec.h
#ifndef GUARD_Vec_h
#define GUARD_Vec_h
#include <cstddef>
#include <memory>
template <class T> class Vec {
public:
// member variables
typedef T* iterator;
typedef const T* const_iterator;
typedef std::size_t size_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
// constructors + destructors
Vec() { create(); }
explicit Vec(size_type n, const T& t = T()) { create(n, t); }
Vec(const Vec& v) { create(v.begin(), v.end()); }
~Vec() { uncreate(); }
// methods
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }
void push_back(const T& t) {
if (avail == limit)
grow();
unchecked_append(t);
}
size_type size() const { return avail - data; }
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return avail; }
const_iterator end() const { return avail; }
private:
iterator data;
iterator avail;
iterator limit;
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
#endif GUARD_Vec_h
Vec.cpp
#include <algorithm>
#include <cstddef>
#include <memory>
#include "Vec.h"
using std::allocator;
using std::max;
using std::uninitialized_copy;
using std::uninitialized_fill;
using std::ptrdiff_t;
template <class T> void Vec<T>::create()
{
data = avail = limit = 0;
}
template <class T> void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data, limit, val);
}
template <class T> void Vec<T>::create(const_iterator i, const_iterator j)
{
data = alloc.allocate(j - i);
limit = avail = uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate()
{
if (data) {
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data, limit - data);
}
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow()
{
size_type new_size = max(2 * (limit - data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = uninitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template <class T> void Vec<T>::unchecked_append(const T& val)
{
alloc.construct(avail++, val);
}
Why doesn't this compile?
The problem is with any and all of the template functions. When you write a Template function, the compiler instantiates the template--that is, it creates a specific version of the function with the types you need to run the code. So when you call
Vec<int> v;
The compiler makes code for a Vec and any function of that type you call. The compiler needs to have access to the templated function definitions while making the code for the main file, because it has to know what kind of code to make for the object file before it goes to link the other files.