I'm a newbie in c++ and I have this piece of code:
#include "passenger.h"
#include "plane_list.h"
int main(){
plane_list<T>::plane_list();
plane_list<T>::add();
}
and I don't understand nor can I seem to find an answer online why I get these errors:
error: ‘T’ was not declared in this scope
plane_list::plane_list();
error: template argument 1 is invalid
plane_list::plane_list();
error: invalid type in declaration before ‘;’ token
plane_list::plane_list();
error: ‘::add’ has not been declared
plane_list::add();
This is the header file:
template <class T> class plane_list{
friend class planes;
public:
plane_list();
~plane_list();
int search(const std::string &flight_code);
plane_list<T> del(const std::string &flight_code);
plane_list<T> add();
private:
T *element;
int length;
};
template <class T> plane_list<T>::plane_list(){
element = new T[100];
length=0;
}
template <class T> int plane_list<T>::search(const std::string &flight_code){
for(int i=0;i<length;i++)if(element[i]._flight_code==flight_code)return i;
return 0;
}
template <class T> plane_list<T> plane_list<T>::del(const std::string &flight_code){
if(search(flight_code)!=0){
for(int i=search(flight_code); i<length; i++)element[i-1]=element[i];
length--;
return *this;
}
else
std::cerr<<"Did not find flight"<<std::endl;
}
template <class T> plane_list<T> plane_list<T>::add(){
element[length]=planes::planes();
length++;
return *this;
}
Any help would be appreciated.
The issue is that there is no type T. I am assuming that plane_list is some template class like this:
template<typename T>
class plane_list{
//...
}
Here the typename T is a placeholder for some type that will be provided when you instantiate the class.
You are getting an error because you are trying to instantiate the plane_list with a type that does not exists.
To use your class correctly you need to change T to some other type:
//for example you could use an int
int main(){
plane_list<int>::plane_list();
plane_list<int>::add();
}
Without knowing the contents of plane_list.h I cannot infer what you are actually trying to do.
EDIT:
As suggested in the comments. You are using the incorrect syntax for instantiating and using your variable. The correct usage would be:
int main(int argc, char** argv){
//create a variable of type plane_list<int> names "list:
plane_list<int> list; //default constructor is called automatically
list.add(); ///call the member function "add"
}
Additionally your code has a memory leak, you allocate the array 'element' with dynamic storage by calling new in your constructor, but never call delete element anywhere which causes the leak. There are several ways to fix this:
Add a destructor and call delete element in it.
Use std::vector<T> if you need to resize the array.
Use std::unique_ptr<T> if you need a fixed length array of runtime determined length.
Don't use dynamic memory since you are allocating an array with compile time size.
I will not provide an example of each of these, but I would recommend reading up on memory management in C++.
Go through your header and be sure to correct the your function call syntax. There are a few places where you are using the same incorrect syntax within your class. For example:
template <class T> plane_list<T> plane_list<T>::add(){
//element[length]=planes::planes();
element[length]=planes{}; //default initialize a "planes" object
length++;
return *this;
}
Related
I am running into a design issue with my code and I am not sure where to go. I am attempting to write a basic I/O class to write vectors of data into a text file for convenience in my research. In practice, I would like to not worry about the datatype in the array that I am writing to file and use the same interface regardless of if it is an array of ints, doubles, etc.
My basic idea was to create an AbstractColumn class, with a templated derived class to handle columns of different data. My I/O class could then contain an array of pointers to objects of this Abstract Class, and I can can add to this array as needed. See my header file below for the implementation of this.
#ifndef BASICIO_H
#define BASICIO_H
#include <vector>
#include <cstring>
#include <string>
#include <stdlib.h>
struct AbstractColumn{
virtual ~AbstractColumn() = 0;
template <class T> T* get_data(); // Issue comes in here, I think
int nrows=0;
};
template <class T>
class Column : public AbstractColumn {
public:
Column(std::vector<T>& v);
~Column();
T* get_data() {return data;}
T* data;
int nrows;
};
template <class T>
Column<T>::Column(std::vector<T>& v){
nrows = v.size();
data = static_cast<T*>(malloc(sizeof(T)*v.size())); //malloc returns void* so we need the static cast
std::memcpy(data, v.data(), v.size());
}
template <class T>
Column<T>::~Column() {}
class BasicIO {
public:
BasicIO(std::string outname) : fname(outname), ncols(0) {}
~BasicIO();
template <class T>
void attach(std::vector<T>& v, std::string name="");
void write();
inline int get_ncols() {return ncols;}
inline std::string filename() {return fname;}
private:
std::vector<AbstractColumn*> columns;
std::vector<std::string> column_names;
std::string fname;
int ncols;
};
template <class T>
void BasicIO::attach(std::vector<T>& v, std::string name){
Column<T>* col = new Column(v);
columns.push_back(col);
column_names.push_back(name);
}
#endif
My issue is coming in in trying to write the write() method that actually dumps this data to a file.
void BasicIO::write(){
// other formatting code here
for(int i = 0; i<columns.at(0)->nrows; i++){
for(int j = 0; j<ncols; j++){
outfile<<std::scientific<<std::left<<std::setw(col_width)<<(columns.at(j)->get_data())[i]<<" ";
} //^ this is causing an error
outfile<<std::endl;
}
outfile.close();
}
When I try to compile, I get the an error saying note: template argument deduction/substitution failed and couldn't deduce template parameter 'T'. Now, I think I understand what the issue is. When the compiler reaches this point, all it doesn't know what type of data is going to be returned by my get_data() function, since in the base class the return type is the template T. However, I don't see a way around this and I am very stuck. I have done a bit of reading and it seems like type erasure might be the way to go, but I also think I might be missing something simpler.
My Question
All of the above context aside, my concrete question is as follows: What is the best way to have an array which holds objects (in this case columns) of generic types, specifically for the application described above? I am happy to offer any other details as needed, I just felt that my code snippets were already quite long. Thank you for reading.
I have the incomplete class below (but the necessary is there to understand my concern).
The following method is copying the content of a given bag (called sac in my code)
template <class T, int capInitial>
Sac<T,capInitial>& Sac<T,capInitial>::
operator+=(Sac &b) {
for(int i=0; i<b.getTaille(); i++){
*this += b.sac[i]; //LINE i DON'T UNDERSTAND
}
return *this;
}
Since the class Below has 2 attributes and a pointer to an array. In the line mentioned above what mechanism enable to take all elements in the given argument and just add it to the array of the class via *this+=b.sac[i], i would have done it in the following way
for(int i=0; i<b.getTaille(); i++){
sac[taille++]= b.sac[i];
Or perhaps there is something i don't quite understand? here is the incomplete class
template <class T, int capInitial>
class IterateurSac;
template <class T, int capInitial=64>
class Sac {
private:
T* sac;
int taille;
int capacite;
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution;
void augmenterCapacite(int cap);
void copier(const Sac &b);
public:
Sac() : taille(0), capacite(capInitial), generator(7437843) {
sac= new T[capacite];
}
template <class T, int capInitial>
Sac<T,capInitial>& Sac<T,capInitial>::
operator+=(const T &element) {
if (taille==capacite)
augmenterCapacite(2*capacite);
sac[taille++]= element;
return *this;
}
}
In C++, you can create your own behaviour for operators. Your class would need to overload operator+= to make this work, and that function would name the member variable to modify.
Indeed, we can see from your first code snippet that the class is already overloading operator+= to take an argument of type Sac<T,capInitial>&; it must have another one taking an argument of type T (or compatible).
What's not clear is why the class definition you later show us does not include a declaration for either of those functions.
I am having a problem with overloading the subscript operator in a class template. I have the following header file to declare the class template (I only include the relevant function):
arrayListType.h
template <class elemType>
class arrayListType {
public:
arrayListType<elemType>& operator[](int);
arrayListType(int size = 100);
virtual ~arrayListType();
protected:
elemType *list; // array to hold the list elements
int length; // variable to store the length of the list
int maxSize; // variable to store the maximum size of the list
};
template <class elemType>
arrayListType<elemType>& arrayListType<elemType>::operator[](int index) {
assert(0 <= index && index < length);
return list[index];
}
And I have the following code in main.cpp, where felinoTipo is a different derived class with its own attributes and works fine. Also, I did not show function insertEnd in the previous header file but it also works fine.
main.cpp
#include "arrayListType.h"
int main() {
arrayListType<felinoTipo> listaFelinos(20);
felinoTipo felinoTemp1("Tigre", "Tigrillo", 1.1, 1.1);
listaFelinos.insertEnd(felinoTemp1);
listaFelinos[0]; //Line X
return 0;
}
The problem arises in line marked X. When I comment that line out and build the project, no errors are found. But when I include that line of code I get the error invalid initialization of reference of type 'arrayListType&' from expression of type 'felinoTipo' main.cpp. My main intention is to store the reference obtained by the operator overloading function in an array of pointers of a base class of the felinoTipo to use virtual functions to print out the objects' attributes.
Any idea why this problem arises?
Your operator returns this type:
arrayListType<elemType>&
but you want to return
elemType&
The compiler is complaining about the invalid conversion from one to the other.
You need something along these lines:
template <class elemType>
class arrayListType {
public:
typedef elemType element_type;
element_type& operator[](int);
const element_type& operator[](int) const; // good idea to provide const overload
....
};
then
auto& e = listaFelinos[0];
or, if you don't have C++11,
arrayListType<felinoTipo>::element_type& e = listaFelinos[0];
Here's the deal. I've looked on this forum and I didn't find the information I'm searching for or I'm probably not able to repeat it for my problem. I have a class Table which is generic and I have a class named MyString.
template <typename typeGen, int DIM>
class Table {
public:
TableauGenerique() : index_(0) { //On initialise courant à 0
}
void add(typeGen type);
private:
typeGen tableGen_[DIM];
int index_;
};
My problem is with the add function.
I sometimes have to do this in the main.cpp: (which works well)
Table <float,6> tabFloat;
tabFloat.add(1.6564);
and at one point, I need to do this which doesn't work because I need to specialize the add function to create an object of MyString, to pass it the string and then store the object in the array (tableGen) :
TableauGenerique <MyString,4> tabString;
So I tried this (after the class), without success.
template <typename typeGen, int DIM>
void Table<typeGen,DIM>::add(typeGen type){ //Which is the generic one for float or ints
if(index_ < DIM) {
tableGen_[courant_] = type;
index_++;
}
}
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type) { //(line 75) Which is the specific or specialized function for myString
MyString str(type);
if(index_ < DIM) {
tableGen_[courant_] = str;
index_++;
}
}
So, How can I make this work because it doesn't compile at all, saying: line75 : error: expected initializer before '<' token and in the main it says not matching function to call Table::add(const char[6]),
I hope everything is clear enough. Let me know if somethings is unclear.
Thank you very much for your help !
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type)
You're trying to specialize add() when in fact it is not a function template to begin with. How do you expect it to work?
You probably meant: (specialization of the class)
template <int DIM>
void Table<string,DIM>::add(string type)
But then this is allowed only if you specialize the class itself. Without specializing the class, the above code would give compilation error!
EDIT:
You can read these online tutorials:
Introduction to C++ Templates
14.5 — Class template specialization
Template Specialization and Partial Template Specialization
Explicit specialization (C++ only)
If you can control the code of the MyString class, you can provide constructors that act as implicit conversions from float to MyString. An example:
#include <string>
#include <sstream>
#include <iostream>
class MyString {
public:
MyString(float number) {
std::stringstream buffer;
buffer << number;
value = buffer.str();
}
void print() {
std::cout << value << std::endl;
}
private:
std::string value;
};
template <class T>
class Foo {
public:
void DoStuff(T item) {
item.print();
}
};
int main() {
Foo<MyString> foo;
foo.DoStuff(1.342); // implicitly converts float to MyString
return 0;
}
This way, you do not need any specialization of the add method. However, implicit conversions are tricky, and you have be careful not to invoke them accidentally, and they may create ambiguities.
EDIT: Upon a second thought, my suggestion below is basically equivalent to
Table<MyString,4> tabString;
tabString.add(MyString("whatever"));
and therefore excessive and/or does not solve the problem. Feel free to ignore :)
I would extend the class Table with a generic method to add something from which you can construct an object of the desired type:
template <typename typeGen, int DIM>
class Table {
public:
Table() : index_(0) {}
void add(typeGen type);
// The additional method
template<typename T> void add(const T& src);
private:
typeGen tableGen_[DIM];
int index_;
};
template<typename typeGen, int DIM>
template<typename T>
void Table<typeGen,DIM>::add(const T& src) {
if(index_ < DIM) {
tableGen_[courant_] = typeGen(src);
index_++;
}
}
Note construction of a temporary typeGen object before the assignment.
Assuming that MyString object can be constructed from a string literal, i.e. from const char*, you can then use it as following:
Table<MyString,4> tabString;
tabString.add("whatever");
or if the above assumption is wrong, the following should probably work (because you constructed a MyString instance from a string instance):
tabString.add(string("whatever"));
So basically the assignment was we had to create a doubly linked list that's templated generically instead of locked to a single data type. I've tried compiling both with gcc and msvc and both compilers are giving me roughly the same errors so I'm assuming its just my bad coding and not the quirkyness of one compiler or the other.
Currently, I'm getting errors saying that my classes in linkList.h are not a template
../linkList.h:34: error: ‘llist’ is not a template type
../linkList.h:143: error: ‘iter’ is not a template type
../josephus.cpp:14: error: ‘llist’ is not a template
../josephus.cpp:14: error: aggregate ‘llist ppl’ has incomplete type
and cannot be defined ../josephus.cpp:15: error: ‘iter’ is not a
template
linkList.h
template<typename T>
class iter
{
public:
iter()
{
position = sentin;
container = sentin->payload;
}
T get() const
{
assert(position != sentin);
return position->payload;
}
void next()
{
position = position->next;
}
void previous()
{
position = position->prev;
}
bool equals(iter itr) const
{
return position == itr.position;
}
private:
node *position;
llist *container;
};
josephus.cpp
llist<int> ppl;
iter<int> pos;
int start = static_cast<int>(argv[1]) - 1;
int end = static_cast<int>(argv[2]) - 1;
Any help in this matter is much appreciated
Your forward declaration says llist is a class:
class llist;
Then you say it is a template:
template<typename T>
class llist;
Similarly with iter.
I don't know how you could make it compilable easily. However, you can make node and iter 'inside' of llist.
There are several issues.
class A;
is not the way you forward declare a templated class.
If A has a single templated parameter you need to say:
template<typename T>
class A;
If you say that after you've already said class A; you're contradicting yourself. The next issue is simlar, friend class A; if A is templated won't work, you need to say friend class A<T>; or similar. Finally, static_cast<int>(argv[1]) will not compile (althought static_cast<int>(argv[1][0]) would, but is still not want you want). To convert a string to an integer meaningfully, you'll need to use atoi, strtol, stringstream etc.
The llist is not a class. So forward declaring it is not usefull.
template<typename T> class llist;
Trying to make the code compile is relatively simple.
You have just missed the template part of a lot of the types. Search for iter llist and node and make sure they have the appropriate on the end.
If you look at the STL it is conventinal to typedef some internal types for ease of use. You could follow the same principle.
template<typename T>
class llist
{
typedef iter<T> Iter;
typedef node<T> Node;
// The rest of the code.
};