So I am creating a Stack class for an assignment in C++. The core of the assignment is to familiarize us with templates. I have read my book over and over and looked question after question on here.
I need to have my Stack class be able to be constructed by
Stack s2;
but I get an error when I compile my test.cpp and can only compile when i construct as
Stack<T> s1;
where T is a std::string, int, etc. How do build my Stack so I can use both constructors?
Stack.cpp
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
using namespace std;
template<typename T>
class Stack {
public:
Stack();
void Push(T val);
T Pop();
void Print();
private:
vector<T> vecT;
};
template <typename T>
Stack<T>::Stack() { }
template <typename T>
void Stack<T>::Push(T val) { vecT.push_back(val); }
template <typename T>
T Stack<T>::Pop() { vecT.pop_back(); }
template <typename T>
void Stack<T>::Print() {
cout << "[ ";
for(int i=0; i<vecT.size(); i++) {
cout << vecT[i] << " ";
}
cout << "]";
}
test.cpp
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
#include "Stack.cpp"
using namespace std;
int main() {
Stack<string> s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
What about a default template parameter?
template<typename T = std::string>
class Stack {
Anyway, with
Stack<T> s1;
and
Stack s2;
you're not using different constructors; you're using, in both cases, the same default (no arguments) constructor.
Instead of templating everything you could just use a stack of a variant type (since C++17, before use Boost.Variant). With the code below you can now push int, double, and std::string onto the stack. Furthermore, Pop() is missing a return statement. Also, pop_back() of vector returns nothing.
#include <iostream>
#include <vector>
#include <string>
#include <variant>
class Stack {
using Variant = std::variant<int,double,std::string>;
public:
Stack();
void Push(Variant val);
void Pop();
void Print();
private:
std::vector<Variant> vecT;
};
Stack::Stack() : vecT() {}
void Stack::Push(Variant val) { vecT.push_back(val); }
void Stack::Pop() { vecT.pop_back(); }
void Stack::Print() {
std::cout << "[ ";
for ( auto const& v : vecT )
std::visit([] (auto&& arg) { std::cout << arg << " "; }, v);
std::cout << "]\n";
}
int main() {
Stack s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
With Boost.Variant you get a C++98 compatible solution.
#include <iostream>
#include <vector>
#include <string>
#include <boost/variant.hpp>
class Stack {
typedef boost::variant<int,double,std::string> Variant;
typedef std::vector<Variant>::iterator Iterator;
std::vector<Variant> vecT;
struct visitor : public boost::static_visitor<void>
{
template < typename T >
void operator()(T const& arg) const { std::cout << arg << " "; }
};
public:
Stack();
void Push(Variant val);
void Pop();
void Print();
};
Stack::Stack() : vecT() {}
void Stack::Push(Variant val) { vecT.push_back(val); }
void Stack::Pop() { vecT.pop_back(); }
void Stack::Print() {
std::cout << "[ ";
for ( Iterator it = vecT.begin(); it != vecT.end(); ++it )
boost::apply_visitor( visitor(), *it );
std::cout << "]\n";
}
int main() {
Stack s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
You could use a default template parameter:
template<typename T = int>
class Stack {
Then you could construct using:
Stack<> s2;
Also, the constructors are identical, you aren't calling a different one each time, but rather the template argument differs.
Related
hey guy this my code on file h
and i need to create method that need to print the type.
#include <iostream>
#include <typeinfo>
using namespace::std;
template <class T>
class Set {
T* group;
int size_group;
public:
Set():group(NULL),size_group(0){};
Set(T*,int);
Set(const Set<T>&);
~Set();
void PrintObj()const;
bool isThere(const T&)const;
void getType()const;
};
template <class T>
void Set<T>::getType() const{
cout << "The type is: " << typeid(*this).name() << endl;
}
Main:
#include "Set.h"
int main() {
int arr[] = {1,4,5,6,3,2};
int arr2[] = {5,2,6};
Set<int> j(arr,(sizeof(arr)/sizeof(arr[0]))),l(arr2,sizeof(arr2)/sizeof(arr2[0])),k;
j.getType();
}
OUTPUT:
The type is: 3SetIiE
how i can print the type name?
It kind of gibberish to me
What you see is calle a mangled name. You can use boost::core::demangle to demangle it (https://www.boost.org/doc/libs/1_73_0/libs/core/doc/html/core/demangle.html).
std::cout << boost::core::demangle(typeid(*this).name()) << std::endl;
I want to pass a pointer to a class method and call that function from an iterator. The code below fails to compile when I include a derived object.
I've tried using a typename for the class specifier (e.g. TC::*pf) but this doesn't work. Can someone suggest how to make this work?
#include <iostream>
#include <vector>
#include <algorithm>
using std::cout;
using std::endl;
using std::vector;
class Base {
public:
Base(int bval) : bval_(bval) { }
virtual void print() {
cout << "Base: bval:" << bval_ << endl;
}
protected:
int bval_;
};
class Derived : public Base {
Derived(int bval, int dval) : Base(bval), dval_(dval) { }
virtual void print() {
cout << "Derived: bval:" << bval_ << " dval:" << dval_ << endl;
}
private:
int dval_;
};
typedef vector<Base*> MyVecType;
typedef MyVecType::iterator MyVecTypeIter;
template <typename T>
void testFunc(MyVecType& v, T (Base::*pf)()) {
for (MyVecTypeIter iter = v.begin(); iter != v.end(); ++iter) {
((*iter)->*pf)();
}
}
int main() {
MyVecType bvec;
bvec.push_back(new Base(44));
bvec.push_back(new Base(55));
// above compiles and runs ok, but this fails to compile
// with 'no matching function' error:
bvec.push_back(new Derived(66));
testFunc(bvec, &Base::print);
return 0;
}
line
bvec.push_back(new Derived(66));
does not compiler because Derived class constructor is private and is defined to take two int arguments.
Fix that and it will compile and run:
http://cpp.sh/9nrkc
I have such header file declaration
#ifndef CONTAINER_H
#define CONTAINER_H
#include <map>
#include "object.h"
namespace memory {
template<class T>
class Collector {
};
template <class T>
class Collector<T*> {
private:
std::map<std::string, T*> mem_storage;
public:
std::string put_object(T* to_store);
T* get_object(std::string key);
void list_objects();
void clean_up();
void clean_object(std::string key); // force cleaning
void mark_object_to_remove(std::string key); // mark unused
void clean_removable(); // move to scheduled task
~Collector() {};
};
}
#endif
And such implementation
#include "../headers/container.h"
#define null nullptr
#include <iostream>
#include <string>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
using namespace memory;
template<typename T>
void Collector<T*>::clean_object(std::string key) {
int removed = mem_storage.erase(key);
std::string removed_string = boost::lexical_cast<std::string>(removed);
}
template<typename T>
void Collector<T*>::clean_up() {
mem_storage.clear();
}
template<typename T>
T* Collector<T*>::get_object(std::string key) {
typename std::map<std::string, T*>::iterator it = mem_storage.find(key);
if (it != mem_storage.end()) {
return it->second;
} else {
return null;
}
}
template<typename T>
void Collector<T*>::list_objects() {
for (typename std::map<std::string, T*>::iterator it = mem_storage.begin(); it != mem_storage.end(); ++it) {
std::cout << it->first << " => " << it->second->to_string() << std::endl;
}
}
template<typename T>
std::string Collector<T*>::put_object(T* to_store) {
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string key = boost::uuids::to_string(uuid);
mem_storage[key] = to_store;
return key;
}
template<typename T>
void Collector<T*>::mark_object_to_remove(std::string key) {
mem_storage.find(key)->second->removable = true;
}
template<typename T>
void Collector<T*>::clean_removable() {
typename std::map<std::string, T*>::iterator it = mem_storage.begin();
while (it != mem_storage.end()) {
std::string id = it->first;
T instance = it->second;
if (instance->removable) {
mem_storage.erase(it++);
} else {
++it;
}
}
}
My main file looks like this
#include <iostream>
#define byte unsigned char
#define null nullptr
#include "base/headers/object.h"
#include "base/headers/indexed_object.h"
#include "base/headers/container.h"
#include "base/headers/reflector.h"
void garbage_collection_prototype_example() {
core::Object *io = runtime::RuntimeReflector::create_instance(
"core::IndexedObject");
core::Object *io_new = runtime::RuntimeReflector::create_instance("core::IndexedObject");
std::cout << "Class 1 name: [" + io->get_class_name() + "]" << std::endl;
std::cout << "Class 2 name: [" + io_new->get_class_name() + "]" << std::endl;
bool is_same = io_new->is_same_instance(io);
std::cout << "Is same: " << std::boolalpha << is_same << std::endl;
memory::Collector<core::Object*> *garbage_collector = new memory::Collector<core::Object*>();
std::string key = garbage_collector->put_object(io);
std::string key_new = garbage_collector->put_object(io_new);
garbage_collector->list_objects();
garbage_collector->mark_object_to_remove(key);
garbage_collector->clean_removable();
garbage_collector->list_objects();
garbage_collector->clean_up();
}
int main(int argc, char *argv[]) {
garbage_collection_prototype_example();
return 0;
}
But I have bunch of errors when I try to build project
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:22: undefined reference to `memory::Collector<core::Object*>::put_object(core::Object*)'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:23: undefined reference to `memory::Collector<core::Object*>::put_object(core::Object*)'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:24: undefined reference to `memory::Collector<core::Object*>::list_objects()'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:26: undefined reference to `memory::Collector<core::Object*>::mark_object_to_remove(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:27: undefined reference to `memory::Collector<core::Object*>::clean_removable()'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:29: undefined reference to `memory::Collector<core::Object*>::list_objects()'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:31: undefined reference to `memory::Collector<core::Object*>::clean_up()'
It's like it can't see any of methods of Collector.
Could it be due to my empty declaration of base non-specialized class Collector? But I tried to add there implementations of the methods and it did not help.
Short answer
You can't put the definition (implementation) of function templates into a .cpp file. Put them into the same header where they are declared.
Explanation
This is because function templates get instantiated when (where) they are used, so the compiler needs to see how they are defined in any place they are used. There are some workarounds to take definition separate from declaration of function/class templates, but all of them imply the inclusion of the definition of the template into the translation unit in which it is used.
Have a problem about how to call the generic template version in a specialization version.
Here is the sample code. But the "vector::push_back(a)" calls itself recursively.
#include <iostream>
#include <vector>
using namespace std;
namespace std
{
template<>
void vector<int>::push_back(const int &a)
{
cout << "in push_back: " << a << endl;
vector::push_back(a); // Want to call generic version
}
}
int main()
{
vector<int> v;
v.push_back(10);
v.push_back(1);
return 0;
}
When you create specialization for some template (no difference class of function), you tell to compiler to generate that one instead of general. So in fact if you have specialization you have no general version for that specialization and you can't call it, because it doesn't exists.
You can simply extract the code into another template function:
template<typename T>
void baseF(T t) { ... }
template<typename T>
void F(T t) { baseF<T>(t); }
template<>
void F<int>(int t) { baseF<int>(t); }
Well, to complement, I think it works for template function specification in some situations.
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
virtual int test() {return 0;}
};
class Derived : public Base
{
public:
virtual int test() {return 1;}
};
template<class T>
void TestOutput(T* a)
{
cout << a->test() << endl;
}
template<>
void TestOutput(Derived* a)
{
cout << "something else" << endl;
TestOutput<Base>(a);
}
int main()
{
Derived d;
TestOutput(&d);
}
I compiled it with visual studio 2013 and the output is:
something else
1
Although I don't think you can always find a TestOutput function of Base to call the generic one.
I wrote my bind function which returns a nullary functor because I don't have boost. Though this code compiles fine, it does not behave as I expected. When I input 2 as the number of numbers and try to enter them, the program terminates the first time I hit return. And, when I debug, it segfaults inside mem_fun_t::operator(). What am I doing wrong here? And how to rectify it?
#include <iostream>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <iterator>
#include <functional>
using namespace std;
namespace MT
{
template<class Operation>
struct binder
{
protected:
Operation _op;
typename Operation::argument_type _arg;
public:
binder(Operation& fn, typename Operation::argument_type arg)
:_op(fn), _arg(arg)
{
}
typename Operation::result_type operator()()
{
return _op(_arg);
}
};
template<class Operation, class Arg>
binder<Operation> bind(Operation op, Arg arg)
{
return binder<Operation>(op, arg);
}
};
int main()
{
vector<int> vNumbers;
vector<char> vOperators;
int iNumCount = 0;
int iNumOperators = 0;
cout << "Enter number of number(s) :) :\n";
cin >> iNumCount;
int iNumber;
cout << "Enter the " << iNumCount << " number(s):\n";
generate_n(back_inserter(vNumbers), iNumCount, MT::bind(mem_fun(&istream::get), &cin));
system("clear");
copy(vNumbers.begin(), vNumbers.end(), ostream_iterator<int>(cout, " "));
cout << endl;
}
istream::get is not the right method to use, as it reads characters, not ints. You'd need to use operator>> (and selecting the right overload is very verbose), or just write a different functor:
template<class T>
struct Extract {
istream &stream;
Extract(istream &stream) : stream (stream) {}
T operator()() {
T x;
if (!(stream >> x)) {
// handle failure as required, possibly throw an exception
}
return x;
}
};
// ...
generate_n(back_inserter(vNumbers), iNumCount, Extract<int>(cin));