I am trying to create a map for which the value is a class I have defined in another file; namely, FoodItemLookup. However, C++ is giving me the error:
template argument 1 is invalid
I find this odd, because I'm not using any templates.
I would appreciate it if someone could explain to me what I am doing wrong. I don't think I'm allowed to post all of the code, but here are the snippets in question:
The attempt at making a map:
std::map<string, FoodItemLookup> foodCatalogue;
A brief rundown of the FoodItemLookup.h file:
#ifndef FOODITEMLOOKUP
#define FOODITEMLOOKUP
#include <string>
class FoodItemLookup
{
private:
...
public:
//constructor
FoodItemLookup(std::string upc, int shelfLife, std::string name);
//copy constructor
FoodItemLookup(const FoodItemLookup &other);
//destructor
~FoodItemLookup();
...
);
#endif
You should have std:: in from of string in your first template argument:
std::map<std::string, FoodItemLookup> foodCatalogue;
"I find this odd, because I'm not using any templates."
Actually std::map uses templates. That's why you need to pass 2 types (here, std::string and FoodItemLookup) between the <> when instantiating your std::map.
More information on templates.
Related
Lets say we have this class
class IntArray {
string name;
};
and we have this driver
int main(){
IntArray xe;
return 0;
}
Basically, how would we store that name of the instance, the "xe" through the constructor and into the data member "string name"?
C++ does not support doing this. Variable names are only something you as a developer are aware of. The compiled program doesn't have them. Your std::string name field inside the class IntArray would not hold "xe"; it would just be uninitialized.
You could, however, use a map - an std::unordered_map<std::string, IntArray> arrays to be exact - and then use arrays["xe"] to access the array you like using a runtime-defined string. See std::unordered_map on CPPReference for details.
#include <string>
class IntArray
{
std::string name;
public:
IntArray(std::string name) : name{ std::move(name) } {}
};
int main()
{
IntArray xe{ "xe" };
}
Well, you have some trick to solve your problem. Instead of using directly the constructor, just use a macro, like:
#define DECL(x) x(#x)
in some common header you #include in your application. Instead of declaring
IntArray ex;
do
IntArray DECL(ex);
on expansion, you will get something like:
IntArray xe("xe");
you can also use the variadic macro expansion of latest standards to be able to call a different constructor.
#define DECL(name, ...) name(#name, #__VA_ARGS__)
If you want, you can also include the type in the macro, so:
#define DECL(type, name) type name(#type, #name)
and declare your variable with:
DECL(IntArray, ex);
That will expand to:
IntArray ex("IntArray", "ex");
this trick is far from complete, but can help you to avoid mistakes because of mispelling variable names as a consequence of having to write twice in source code.
I have the following code in myclass.h file:
typedef std::unordered_set< int, int> Parameters;
class MyClass
{
public:
void myFunction();
private:
Parameters* m_params;
}
Then, myFunction looks as follows:
void MyClass::myFunction()
{
...
m_params->emplace(1,1);
}
When I try to compile, I get:
term does not evaluate to a function taking 1 arguments
If I comment the emplace line, the error disappears. However, I don't find any misuse related to this function signature:
http://en.cppreference.com/w/cpp/container/unordered_map/emplace
Any help would be much appreciated.
Simple typo: You used std::unordered_set in your code, but you meant std::unordered_map.
Your implementation doesn't recognise any error setting Hash to int in the std::unordered_set template until you try to put something into the container. Then it tries to hash the input, and realises it can't use an int as a function.
I'm making a simple Error class that should be thrown using throw statement, logged or written in console. The basic idea of the usage is:
//const char* filename is function argument
if(!file_exists(filename))
throw Error(line, file, severity)<<"Invalid argument: the file does not exist: "<<filename;
I originally wanted to extend stringstream, but my searches indicate that it's impossible to extend on streams.
I'd really like to make the error class as convenient as you can see above, which means able to "eat" various types and append them to internal error message.
So actually it's not so hard. You can't inherit from stringstream directly - or I just didn't find out how. You'll soon drown in std template system...
template<class _Elem,
class _Traits,
class _Alloc>
// ... mom help me!
But, you can make a template:
template <class T>
Error& Error::operator<<(T const& value) {
//private std::stringstream message
message<<value;
//You can also trigger code for every call - neat, ain't it?
}
You'll be adding all values from this method to private variable within the class:
class Error
{
public:
Error(int, const char*, int);
~Error(void);
std::string file;
unsigned int line;
//Returns message.str()
std::string what();
//Calls message.operator<<()
template <class T>
Error& operator<< (T const& rhs);
private:
std::stringstream message;
};
There's still something I'm missing - how to distinguish between types supported by stringstream and others and throw error on the line of call, rather than in Error class. Also I'd like to write own handlers for unsupported types.
I just finished creating my binary tree class only to realise that I was supposed to make it as a template. I've spent hours now trying to get to convert it to a template, but I keep getting a multitude of errors ranging from "invalid use of template-name" to "extra qualification of a member". I am new to the concept of templates but I have an idea of what I'm trying to achieve.
BTree.cpp
#include <iostream>
#include <string>
#include "BTree.h"
using namespace std;
BTree::BTree(board startboard, string startplayer)
{
treeboard = startboard;
player = startplayer;
root = new BTNode(treeboard, player,-1,-1);
}
BTree::~BTree()
{
delete root;
}
int BTree::sumtree()
{
if (root->getvalue(-1) > root->getvalue(1))
return root->getchildposition(-1);
else
return root->getchildposition(1);
}
BTree.h
#include <string>
#include "BTNode.h"
#include "board.h"
using namespace std;
class BTree
{
public:
BTree(board startboard, string startplayer);
~BTree();
int sumtree();
private:
string player;
board treeboard;
BTNode *root;
};
'startplayer' is currently a string, I would like this to be the generic template type.
What should my process be for turning this into a single template file?
Well, let's first look what errors or other deficits your code has:
Your BTree has a custom dtor, because it holds resources. But you are violating the rule-of-3:
You need to define or delete, at minimum, your assignment-operator and copy-ctor.
As an alternative (preferable), change BTNode *root; to use std::unique_ptr.
The first thing included in the implementation-file should always be its own header, to find broken dependencies in the header.
The header should include everything neccessary to use it, and not one bit more.
Now, there are good reasons why templates are usually header-only:
The compiler needs the definition to instantiate it.
Use the opportunity to move some of the functions into the class.
Next, you define a template like this:
template<class T> class BTree {
int BTree::sumtree();
};
And non-inline members like this:
template<class T> int BTree<T>::sumtree() {
return //...
}
In the template, you can use the type-argument like a normal type.
A note on BTNode: It's an implementation-detail of BTree, so put its definition into the class (which makes templating it the same and using it easier as well.)
Alternatively, if you don't actually need all the template-arguments for BTNode as well (or want to share its implementation), template it separately.
Don't forget to change all references from BTree though.
First of all to use templates in C++ all of your executable code NEEDS to be in the .h file so it is available at compile time.
Then to template your class, the usual way is to make it like :
template<class T>
class BTree
{
Btree(T startPlayer)
{
player = startPlayer;
}
// ... snip ...
T player;
}
My goal is to store all the keys of a map (first item) to a vector and I'm doing the following.
template < class vecDet>
class storeInto
{
public:
storeInto(vecDet& source) : VectorInfo(source) { }
~storeInto();
template <class pairdet>
void operator()(pairdet& pairinfo)
{
VectorInfo.push_back(pairinfo.first);
}
private:
vecDet& VectorInfo;
};
template<class mapDet, class vecDet>
void storeMapToVector(const mapDet& mapContA, vecDet& vecContA)
{
for_each(mapContA.begin(), mapContA.end() , storeInto<vecDet>(vecContA));
}
Finally, from my main program, I'm calling the storeMapToVector() as follows
storeMapToVector<mapinfo,vector<char> >(mapContents, vecContents);
where mapinfo is declared as follows
typedef map<char,int> mapinfo;
Is there a better way to do this? Is there anything wrong with this?
Your code looks like it would work at first glance. However, there's a much simpler way to do this:
I haven't evaluated your code, but there is certainly a much easier way to do what you want built into most STL implementations:
vecContents.resize(mapContents.size());
std::transform(mapContents.begin(), mapContents.end(), vecContents.begin(),
select1st<pair<const char, int> >());
Alternatively:
vecContents.resize(mapContents.size());
std::transform(mapContents.begin(), mapContents.end(), vecContents.begin(),
select1st<mapinfo::value_type>());
There is 1 wrinkle though - select1st is a SGI extension. It's in almost every STL implementation but where varies. For GNU, you have to do:
#include <ext/functional>
using __gnu_cxx::select1st; // or just using namespace __gnu_cxx;
See this answer for using it in Visual Studio.
The code seems fine. Some nitpicks though:
~storeInto();
What do you need the destructor for? The functor could just live without it.
storeMapToVector<mapinfo,vector<char> >(mapContents, vecContents);
The template argument list seems unnecessary here, it could just be
storeMapToVector(mapContents, vecContents);
The template parameters would be inferred in this context.