My function prototype looks as follows:
// Purpose: finds an element in the ArrayList
// Parameters: 'x' is value to be found in the ArrayList
// Returns: the position of the first occurrance of 'x' in the list, or -1 if 'x' is not found.
int find(const T& x) const;
It is placed in the class ArrayList
template <typename T>
class ArrayList
{
private:
int m_size; // current number of elements
int m_max; // maximum capacity of array m_data
T* m_data; // array to store the elements
T m_errobj; // dummy object to return in case of error
public:
int find(const T& x) const;
My definition is:
template <typename T>
int find(const T& x) const
{
int i;
while (m_data[i]!=x && m_data[i]!=NULL)
i++;
if (m_data[i]=x)
return i;
else
return (-1);
}
Whenever I compile, I receive the error in the title, and an error that m_data is not declared in the scope. How do I fix this?
Edit: I changed the definition to
int ArrayList<T>:: find(const T& x) const
I got a ton of errors
int ArrayList:: find(const T& x) const
didn't work either
Templates must be defined in headers. In your case, you are spliting it in .h/.cpp. In order to work, you need to define it together with your class definition. Something like this:
template <typename T>
class ArrayList
{
private:
int m_size; // current number of elements
int m_max; // maximum capacity of array m_data
T* m_data; // array to store the elements
T m_errobj; // dummy object to return in case of error
public:
int find(const T& x) const;
};
#include "x.hpp"
and define it in a file x.hpp
template <typename T>
int ArrayList<T>::find(const T& x) const
{
int i;
while (m_data[i]!=x && m_data[i]!=NULL)
i++;
if (m_data[i]=x)
return i;
else
return (-1);
}
Note that this has the same effect as you have defined everything in a unique header file
Related
I encountered an error that happend in my proxy class for operator []. It was checking if index is in range, and worked fine when I implemented my class template with T** values.
But I felt like change whole implementation to std::vector<std::vector<T>>. Everything is fine, expect said operator[].
Matrix class operator(s)
//***************************************************************************
template <typename T>
X_Proxy<T> Matrix<T>::operator [](const size_t& j)
{
if(j >= y)
ERROR_MSG(Y_OUT_RANGE);
return X_Proxy<T>(inner[j], x);
}
//***************************************************************************
template <typename T>
const X_Proxy<T> Matrix<T>::operator [](const size_t& j) const
{
if(j >= y)
ERROR_MSG(Y_OUT_RANGE);
return X_Proxy<T>(inner[j], x);
}
//***************************************************************************
Proxy class template definition:
template <typename T>
struct X_Proxy
{
X_Proxy(std::vector<T> PTR, const size_t X) : x_ptr(PTR), x(X) {}
T& operator [] (size_t pos);
const T& operator [] (size_t pos) const;
std::vector<T>& x_ptr;
const size_t& x;
};
Proxy class operator(s):
//***************************************************************************
template <typename T>
T& X_Proxy<T>::operator [] (size_t pos)
{
if (pos > x-1)
Matrix<T>::ERROR_MSG(Matrix<T>::X_OUT_RANGE);
return x_ptr[pos];
}
//***************************************************************************
template <typename T>
const T& X_Proxy<T>::operator [] (size_t pos) const
{
if (pos > x-1)
Matrix<T>::ERROR_MSG(Matrix<T>::X_OUT_RANGE);
return x_ptr[pos]; // <--- the error line
}
//***************************************************************************
Matrix error function:
template <typename T>
void Matrix<T>::ERROR_MSG(const int& MSG)
{
std::cerr << info[MSG] << std::endl;
exit(MSG);
}
Compilation error:
..\matrix.h:47: error: returning reference to temporary [-Wreturn-local-addr]
return x_ptr[pos];
^
What could go wrong with our lovely template library?
Your X_Proxy constructor is storing a reference to a temporary:
X_Proxy(std::vector<T> PTR, const size_t X) : x_ptr(PTR), x(X) {}
Here, PTR is a local temporary, and x_ptr is an lvalue reference:
std::vector<T>& x_ptr;
This isn't standard C++, so it shouldn't even compile. But your compiler allows it, leaving you with a dangling reference.
Perhaps you want to store a reference to a valid vector:
X_Proxy(std::vector<T>& PTR, const size_t X) : x_ptr(PTR), x(X) {}
^
This will work as long as the vector referred to by PTR outlives the X_Proxy instance.
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 ?
I have a class which I have written its [] operator, and I want that sometimes the operator will return an int and sometimes a struct.
But the compiler won't let me overload the operator, why?
It says:"...cannot be overloaded"
Code:
template <class T> struct part
{ };
template <class T> class LinkedList
{
public:
LinkedList() : size(0), head(0) {}
T& operator[](const int &loc);
part<T>& operator[](const int &loc);
};
template <class T> T& LinkedList<T>::operator[](const int &loc)
{
..a lot of thing which compiles perfectly
}
template <class T> part<T>& LinkedList<T>::operator[](const int &loc)
{
...the same thing but returns struct&.
}
You can't overload a function based on the return type. You could have your operator return a variant of int and string, and let the user check which was actually returned, but its cumbersome. If the return type can be determined at compilation time, you can implement the operator overloads by mean of having different indices types. Something like this:
struct as_string
{
as_string( std::size_t index ) : _index( index ){}
std::size_t const _index;
};
...
int operator[]( int index ) const { ... };
std::string operator[]( as_string const& index ) const { ... };
And then the caller would invoke object[0] to get an int result, or object[as_string(0)] to get a string result.
The type of a function's output is not a part of the function's signature. Thus you can't use both int operator[](int index) and Foo operator[](int index).
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
}
I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.
Here is the definition of the class template
// GenericContainer.hpp
// --------------------------------------
class ContainerItem
{
protected:
virtual ContainerItem& getInvalid() = 0;
public:
virtual ~ContainerItem();
bool isValid() const;
};
template<typename D, typename B>
class IsDerivedFrom
{
static void Constraints(D* p)
{
B* pb = p; // this line only works if 'D' inherits 'B'
pb = p; // suppress warnings about unused variables
}
protected:
void IsDerivedFrom2() { void(*p)(D*) = Constraints; }
};
// Force it to fail in the case where B is void
template<typename D>
class IsDerivedFrom<D, void>
{
void IsDerivedFrom2() { char* p = (int*)0; /* error */ }
};
template <class T>
class GenericContainer : public IsDerivedFrom<T, ContainerItem>
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
// Class to be contained (PURPOSELY, does not derive from ContainerItem)
// Data.hpp
//----------------------------------------------------------------
class Data
{ /* implem details */};
// Container for Data items
// Dataset.h
// ----------------------------------------------------------------------------
#include "GenericContainer.hpp"
#include "Data.hpp"
class Dataset: public GenericContainer<Data>
{
public:
Data& getInvalid();
};
// C++ source
// -----------------------------------------------------------
#include "Dataset.hpp"
Dataset ds;
Can anyone explain why the code above compiles?.
[Edit]
The code above should NOT compile for two reasons:
The class 'Data' does NOT derive from ContainerItem, and yet it can be stored in GenericContainer (as illustrated by the class Dataset). Incidentally, this issue has now been resolved thanks to the answer given by Omifarious and jdv
The class 'Data' does NOT implement the pure virtual method declared in the ABC ContainerItem - using the fixes recommended in the answers below, the first issue (enforcement of policy) is resolved, however the compiler fails to notice that Data does not implement the getInvalid() method of the ContainerItem 'interface'. Why is the compiler missing this glaring mistake?
BTW, compiler and OS details are:
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Change IsDerivedFrom2 to IsDerivedFrom and it fails to compile in just the expected manner.
The problem is that a method from a template class is never instantiated if it isn't called. Changing the name makes it a constructor, so it then ends up being called by the constructors of classes derived from IsDerivedFrom. It will still compile to empty code. The compiler will optimize it away the dead assignment.
I would recommend you not write template code like this yourself if you can manage to use Boost, particularly is_base_of from the Boost type traits library.
In particular, your GenericContainer template can be more simply and easily implemented this way using Boost:
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
template <class T>
class GenericContainer
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
GenericContainer() {
BOOST_STATIC_ASSERT( (::boost::is_base_of<ContainerItem, T>::value) );
}
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
The Constraints function is not generated because IsDerivedFrom2 is never referenced. This is required behavior for C++. Maybe it helps to call it from the constructor. Otherwise, check the boost library for functionality like this.