In my programming class we're just being introduced to the concept of templates within C++. This is a concept we never covered in my class on Java last semester, and the whole syntax of C++ is really throwing me for a loop. I'm getting a long string of compilation errors with the code I will post below. It would make me think that I'm missing something very obvious within the template Syntax. The following is just an example template I'm trying to work with, something to get me started on the homework. If any of you have any insights as to why this isn't compiling, I'd be grateful. Thanks!!
keyValuePair.h
#include <fstream>
#include <iostream>
#include <string>
#ifndef KEYVALUEPAIR
#define KEYVALUEPAIR
template<class key, class value>
class keyValuePair
{
private:
key kvar;
value vvar;
public:
keyValuePair(); //Default Constructor
void setKvar(key object1); //Method to set kvar to a value
void setVvar(value object2); //Method to set vvar to a value
key getKvar(); //Method to return kvar
value getVvar(); //Method to return vvar
};
#include "keyValuePair.cpp"
#endif
keyValuePair.cpp
#include <iostream>
#include <fstream>
#include <string>
#include "keyValuePair.h"
template<class key, class value>;
keyValuePair<key, value>::keyValuePair()
{
}
template<class key, class value>; //return the value of kvar
key keyValuePair<key, value>::getKvar()
{
return kvar;
}
template<class key, class value>; //return the value of vvar
value keyValuePair<key, value>::getVvar()
{
return vvar;
}
template<class key, class value>; //set the value of kvar
void keyValuePair<key, value>::setKvar(key& object1)
{
object1 = kvar;
}
template<class key, class value>; //set the value of vvar
void keyValuePair<key, value>::setVvar(value& object2)
{
object2 = vvar;
}
main.cpp
#include <fstream>
#include <iostream>
#include <string>
#include "keyValuePair.h"
using namespace std;
int main(int argc, char* argv[])
{
fstream myFile(argv[1], ios::in);
fstream fout("out.txt", ios::out);
myFile.close();
fout.close();
keyValuePair<string, int> sample;
sample.setKvar("Hello World.");
sample.setVvar(3);
cout << sample.getKvar() << sample.getVvar() << "\n";
return 0;
}
Remove the semicolon after template<class key, class value>:
!--here
template<class key, class value>;
keyValuePair<key, value>::keyValuePair()
{
}
You have several small mistakes in the syntax.
you declared setKvar and setVvar to take the parameter by value, but defined them to take a reference. (Drop the & in .cpp file)
Do not put a semicolon after template<class key, class value>
in setKvar and setVvar, you have the argumets swapped in the assignment. It should read like kvar = object1; in setKvar and analogically vvar = object2; in setVvar
Do not include the cpp file in the header, include the content directly into the header file, like PorkyBrain said.
keyValuePair.h should not include keyValuePair.cpp. Also the function bodies in keyValuePair.cpp should be declaired directly (different best practaces for templates than normal functions)
template<class key, class value>
class keyValuePair
{
private:
key kvar;
value vvar;
public:
keyValuePair(){} //Default Constructor
void setKvar(key object1){kvar = object1;} //Method to set kvar to a value
void setVvar(value object2){vvar = object2;} //Method to set vvar to a value
key getKvar(){return kvar;} //Method to return kvar
value getVvar(){return vvar;} //Method to return vvar
};
The semicolons after the
template<class key, class value>;
are a typo too.
Also your setKvar function assigns the value of kvar to the parameter taken by reference. I don't this this is what you want seeing as the function is named set.
The reason the bodies of template class member functions are usually declared in line is that the compiler will only generate code for a particular type or types (which is called instantiating the template for the particular type) if it sees them being used in that compilation unit (usually a compilation unit and a .cpp file are the same thing).
This means that if you put the function bodies in keyValuePair.cpp and try to use them in main.cpp the linker will give you "not found" errors because in the compilation unit keyValuePair.cpp the compiler could not see them being used so it never created them.
You can cause the template to be instantiated for specific types directly like this:
template keyValuePair<int,long>;
however this is probably bad style because every time you want to use your template with new types you need to add these declarations to your keyValuePair.cpp file which defeats the purpose of having the flexibility in the first place.
Related
I need to build a vector of a class that can have multiple type like this:
#include <variant>
#include <vector>
#include "Field.h"
using namespace std;
int main()
{
variant <int, float> v;
vector <variant<Field<int>, Field<string>, Field<float>>> fdList;
fdList[0].getName();
}
And this is header file Field.h:
#pragma once
#include <string>
#include <vector>
using namespace std;
template<class T>
class Field
{
public:
Field();
Field(string);
void setName(string);
string getName();
bool isPrime();
void toPrime();
void toForeign(Field);
~Field();
private:
string FD_Name;
vector <T> records;
bool isPrimeK = false;
string message;
};
template<class T>
string Field<T>::getName()
{
return FD_Name;
}
When I try to access getName() function, Visual Studio keeps giving me the following message error:
E0135 class "std::variant<Field, Fieldstd::string, Field>" has no member "getName"
C2039 'getName': is not a member of 'std::variant<Field,Fieldstd::string,Field>'
But it works just fine, if I define my vector like this:
vector <Field<int>> fdList;
fdList[0].getName();
How can I fix this?
For any issue about standard library, I recommend you to check document first.
You can see here about how to use std::variant.
In short, you cannot access the content in your std::variant like that because its type is std::variant but not the types you store in it. For your case, I think you may want to check what's inside first by calling std::variant::index() method, then get the value by calling std::get.
Calling a method on variant doesn't automatically call the method of the active alternative of variant. You'll have to visit the active alternative and invoke the corresponding handler. In your case, since you want to handle all of the potential active alternatives the same way, you can do:
std::visit([](const auto& field) {
field.getName();
// ...
}, fdList[0]);
Alternatively, you can wrap the variant in something like:
struct AnyField {
string getName() const {
return std::visit([](const auto& field) { return field.getName(); }, v);
}
std::variant<Field<int>, Field<string>, Field<float>> v;
};
then use them like you wanted to:
vector<AnyField> fdList;
fdList[0].getName();
The idea is identical to the generic version of GetComponent() in Unity. But I'm currently stumbling on the following template issue:
template<class T> std::shared_ptr<T> MyClass::GetMyComponent()
{
for (int i = 0; i < _baseTypeList.size(); i++)
{
auto base = _baseTypeList[i];
T* check = dynamic_cast<T*>(base.get());
if (check)
{
return std::static_pointer_cast<T>(base);
}
}
return std::shared_ptr<T>(nullptr);
}
where _baseTypeList is a std::vector<std::shared_pntr{MyBaseType}> types.
In this function, I am iterating over a list of components to find if there is one that matches the type I'm asking for. if there is one, return the component cast to that type. Otherwise return a nullptr.
However, when I call this function from outside code, I get the following error:
error C2680: 'MyType*' : invalid target type for dynamic_cast
where MyType is some class that derives from component.
When I put #include "MyType.h" in the header it compiles just fine but without it it gives this error and doesn't compile.
This means I cannot use it in other classes without modifying the header file this template class resides in, which will be a problem for me.
Is there a way I can achieve simular results without having to #include every single header of the type I pass in the template for?
[EDIT]
For clarity, consider a person using my library, he creates a type
"Foo : MyBaseType" where MyBaseType has a virtual method "Update" that is called every frame.
any instance of class MyBaseType (including Foo) is to be managed by this library, and have update called every frame.
This library thus has a large list of "MyBaseType" objects. But has no knowledge of the actual type they are, just that they derive from "MyBaseType", so it can call Update() on them.
If I need a specific type the library needs to be able to search for it in this list and return it.
I would like this "search" to happen in the library itself, so I do not have to expose the list, and write a new "search" method for every type that derives from "MyBaseType"
[FINAL]
It turned out I messed up the include order in my project.
a minimal example of what I was trying to do would be:
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <memory>
#include "vector"
class MyBaseClass
{
virtual void Update(){};
};
class MyLibrary
{
public:
template<class T> std::shared_ptr<T> GetComponent();
std::vector<std::shared_ptr<MyBaseClass>> list;
};
template<class T> std::shared_ptr<T> MyLibrary::GetComponent()
{
static_assert(std::is_base_of<MyBaseClass, T>::value, "T1 is no subclass of ModelComponent");
for (unsigned int i = 0; i < list.size(); i++)
{
auto comp = list[i];
T* check = dynamic_cast<T*>(comp.get());
if (check)
{
return std::static_pointer_cast<T>(comp);
}
}
return std::shared_ptr<T>(nullptr);
}
class MyClass : public MyBaseClass
{
void Update() override;
};
void MyClass::Update()
{
}
int _tmain(int argc, _TCHAR* argv[])
{
MyLibrary lib;
lib.list.push_back(std::make_shared<MyClass>());
auto var = lib.GetComponent<MyClass>();
std::cout << (var ? "var is object" : "var is not") << std::endl;
while (true)
{
}
return 0;
}
which works as expected.
The primary issue was that the compiler gave an error in the "GetMyComponent" function, so I found a usage of it that did everything as suggested.
But it turned out there was a second usage that did not have the definition of "MyClass" before calling it (but didn't give an error, as it was forward declared in its header file).
You don't need the definition of possible T types included into your header. You do need the relevant one defined in the translation unit in which the template is expanded:
// client.cpp
#include <myclass.h>
#include <foo.h> // defines class Foo
void f(MyClass *p)
{
auto c = p->GetMyComponent<Foo>();
c->foobar();
}
I have a template class. Since the templates are processed during compile time, is it possible to compare the template parameter during compile time and use the preprocessor to add specific code? Something like this:
template<class T>
class MyClass
{
public:
void do()
{
#if T is equal to vector<int>
// add vector<int> specific code
#if T is equal to list<double>
// add list<double> specific code
#else
cout << "Unsupported data type" << endl;
#endif
}
};
How can I compare the template types to another type during compile time as shown in the example above? I do not want to add specific subclasses that handle specific types.
First things first - do is a keyword, you can't have a function with that name.
Secondly, preprocessor runs before the compilation phase, so using stuff from templates in it is out of the question.
Finally, you can specialize only a part of a class template, so to speak. This will work:
#include <iostream>
#include <vector>
#include <list>
template<class T>
class MyClass
{
public:
void run()
{
std::cout << "Unsupported data type" << std::endl;
}
};
template<>
void MyClass<std::vector<int>>::run()
{
// vector specific stuff
}
template<>
void MyClass<std::list<double>>::run()
{
// list specific stuff
}
Live demo.
I must be able to use the command below where T can be any type, such as string.
Counter<T> counter;
Counter should be able to hold multiple items so I have chosen to implement this as a vector. Each item must itself consist of a variable of type T (so a string if we continue the above example) and an int. I need to keep the solution as simple as possible as later on I will need to create functions that print out each item by descending int value amongst other tasks. I have had a go with the following code but 1) it doesn't work and 2) is there a better solution?
#include<string>
#include<cstdlib>
#include<vector>
template<class T>
class Record{
T itemtype;
int total;
public:
int increment(T item);
int count(T item);
void printSummary();
};
class Counter{
vector<Record> data;
};
int main(){
Counter<string> counter;
return 0;
}
Your program contains a few mistakes, pointed out below.
Minor issue:
First of all, a minor thing: you don't need to include the <cstdlib> header - at least not for what you are showing.
#include<string>
// #include<cstdlib> // <== (YOU DON'T SEEM TO NEED THIS)
#include<vector>
First problem:
If you use unqualified names to refer to objects that live in a namespace, you should first have a using declaration that lets the compiler resolve those unqualified names to the correct fully qualified names (i.e. including the namespace they belong to).
For instance, vector and string belong to the std namespace. Thus, either you use those names in fully qualified form (std::vector and std::string), or you add proper using declarations, as done below:
using std::vector; // USING DECLARATIONS TO ALLOW UNQUALIFIED NAMES SUCH AS
using std::string; // string AND vector TO BE CORRECTLY RESOLVED
Second problem:
Finally, what you want is to make your class Counter parameterized, and that parameter should be used for instantiating the internal vector. Therefore, Counter must also be a class template (that's how you are using it in your main() function, after all):
// MAKE THIS A CLASS TEMPLATE!
template<typename T>
class Counter{
vector<Record<T>> data;
};
Conclusion:
After all the above corrections have been implemented, this is how your code should look like:
#include<string>
#include<vector>
using std::vector;
using std::string;
template<class T>
class Record{
T itemtype;
int total;
public:
int increment(T item);
int count(T item);
void printSummary();
};
template<typename T>
class Counter{
vector<Record<T>> data;
};
int main(){
Counter<string> counter;
return 0;
}
Here is a live example showing the above piece of code compiling.
You got it wrong, you need to make Counter parameterizable:
template <typename T>
class Counter{
vector<Record<T> > data;
};
Of course, this means that all records in your counter will contain strings.
If "Counter should be able to hold multiple items" really means that one instance of Counter should contain various types or records (e.g. both strings and ints) then you need to use something like VARIANT or boost::any or boost::variant or something similar depending on your requirements and technology that you use, e.g.
Counter<boost::any> counter;
I think you meant to do this:
template<class T>
class Record{
T itemtype;
int total;
public:
int increment(T item);
int count(T item);
void printSummary();
};
template<class RECORDTYPE>
class Counter{
vector<Record<RECORDTYPE>> data;
};
int main(){
Counter<string> counter;
return 0;
}
Record is a template so you cannot just have vector<Record> you must have a vector of a type of record, i.e. vector<Record<T> >
I'm new to C++ and trying to code a HashTable data structure.
I've written it to be generic using templates, and I've included a HashEntry object to use in it to allow for easy quadratic probing for collisions.
The code I have is:
(in a .C file that #include's the below class definition .H file):
HashEntry::HashEntry()
{
this->isActive = false;
}
And the associated .H file with the class definitions is:
#include <iostream>
#include <string>
#include "Entry.C"
using namespace std;
#define Default_Size 50000
class HashEntry;
template <class T> class HashTable
{
private:
int size;
int occupied;
T array[Default_Size];
public:
HashTable();
int Size();
void Add(T t);
void DebugAdd(T t, int index);
T* Get(string index);
/* How do I declare the existence of HashEntry BEFORE here? */
int FindNextOpen(HashEntry he); // Only works for hash_entry objects!
int Hash(string str);
void Rehash();
};
class HashEntry
{
private:
Entry e;
bool isActive;
public:
HashEntry();
HashEntry(Entry e);
bool IsActive();
Entry GetEntry();
};
Whenever I try and compile everything, I get the error for the HashEntry constructor above:
"no matching function for call to Entry::Entry()" ... "candidates are.....".
I have no idea what it means -- when I try to include a default Entry() constructor (my first interpretation), it throws more errors.
Thanks for the help!
UPDATE -- ENTRY.C:
#include "Entry.H"
/* ***Entry Methods*** */
/*
* Overloaded Entry obejct constructor that provides a string value.
*/
Entry::Entry(string s)
{
this->value = s;
this->count = 0;
}
/*
* Returns the number of times this Entry has been accessed/
* found.
*/
int Entry::Count()
{ return this->count; }
/*
* Returns the string value stored in the Entry object.
*/
string Entry::Value()
{ return this->value; }
And the associated .H file with the class definitions is:
#include <iostream>
#include <string>
#include "Entry.C"
Whoa! Never, ever #include a source file in a header.
Your Entry.C should not exist. Instead define the constructor in your header, inside the class definition:
class HashEntry
{
private:
Entry e;
bool isActive;
public:
HashEntry() : isActive(true) {}
...
}
One thing that you haven't shown us is the definition of the class Entry. That is one of the sources of your problem. It's a bit hard to pin down your problem when you didn't show us the very thing that is causing it.
I found the problem.
The error message says there is not matching function call for "Entry::Entry()". Because in no case was I actually creating Entry objects I had no idea what it meant.
I tried adding an explicit default constructor for class Entry and it resolved.
Thanks for the help everyone!