Initializing stl classes with template parameters - c++

I am trying to do declare a stl map with template parameters like so:
( assume T as typename like so : template <class T>)
map<T, T> m; ( in .h file )
It compiles fine. now in my cpp file, when I want to insert into the map, i am not able to. The only methods i get on intellisense are "at" and "swap" methods.
Any ideas? Anyone please?
Thanks in advance.
here is sample code:
#pragma once
#include <iostream>
#include <map>
using namespace std;
template <class T>
class MySample
{
map<T, T> myMap;
//other details omitted
public:
//constructor
MySample(T t)
{
//here I am not able to use any map methods.
//for example i want to insert some elements into the map
//but the only methods I can see with Visual Studio intellisense
//are the "at" and "swap" and two other operators
//Why???
myMap.
}
//destructor
~MySample(void)
{
}
//other details omitted
};

The usual ways to insert key-value pairs into a std::map are the index-operator syntax as well as the insert function. I'll assume std::string for keys and int for values for the sake of the example:
#include <map>
#include <string>
std::map<std::string,int> m;
m["hello"] = 4; // insert a pair ("hello",4)
m.insert(std::make_pair("hello",4)); // alternative way of doing the same
If you can use C++11, you may use the new uniform initialization syntax instead of the make_pair call:
m.insert({"hello",4});
And, as said in the comments, there is
m.emplace("hello",4);
in C++11, which constructs the new key-value pair in-place, rather than constructing it outside the map and copying it in.
I should add that since your question is actually about initialization, rather than inserting fresh elements, and given that indeed you do this in the constructor of MyClass, what you should really do (in C++11) is this:
MySample(T t)
: myMap { { t,val(t) } }
{}
(Here I assume there is some function val which generates the value to store for t in the map.)

Related

Trying to (slightly) generalize C++ template. Associative container Key:Value Inversion

The goal of the function template below is to take any unordered_map and to produce a new unordered_map with the key_type and mapped_type inverted.
The function below works for std::unorderd_map. I'd like it to additionally work for EITHER std::unordered_map and any stl hashmap analog.
An additional benefit I'd like to maintain is that when calling the function, if default behavior is desired, auto inversion = InvertHashMap(someIntStringMap) works with no template arguments. However, if I do provide the valid initial template arguments, I can override the default hasher for example used to build the inverted map.
I am having profound difficulty making the container generic, while still providing default-able template arguments based on that container's 5 template arguments. As soon as I make the container itself a template argument, overload resolution fails and the compilation fails.
I've entertained making the associative container the only template argument, but then the ability to affect the output container's template arguments are lost, at least in the way they can be explicitly templated in my non-flexible example.
#include <unordered_map>
#include <utility>
#include <functional>
#include <memory>
template <typename InKeyType,
typename InValueType,
typename InHasher,
typename InEq,
typename InAlloc,
typename OutHash = std::hash<InValueType>,
typename OutEq = std::equal_to<InValueType>,
typename OutAlloc=std::allocator<std::pair<constInValueType,InKeyType>>>
std::unordered_map<InValueType, InKeyType, OutHash, OutEq, OutAlloc>
InvertMap(const std::unordered_map<InKeyType, InValueType, InHasher, InEq, InAlloc>& source)
{
std::unordered_map<InValueType, InKeyType, OutHash, OutEq, OutAlloc> outMap;
for (const auto& sourceKVPair : source)
outMap[std::get<1>(sourceKVPair)] = std::get<0>(sourceKVPair);
return outMap;
}
//in a .cpp
unordered_map<int,string> um;
auto newUM = InvertHashMap(um); //works well; newUM::key_type is string
I'd like to be able to call InvertMap(aIntStringUnorderedMap) and ALSO InvertMap< int, string, hash<int>, ..., MyCustomStringHasher>(aIntStringHashMapLikeClass)//producing a HashMapLikeClass<string,int, MyCustomStringHasher,...defaults>
TLDR: How do I introduce an argued container to a template, and also its template parameters, without changing call-site semantics?
Edit. This is my attempt to use the container as the only template argument.
template <typename AssocCont>
auto InvertCompliantHashMapThatIsntSTDUnorderedMap(const AssocCont&)
{
typedef typename AssocCont::key_type InKeyType;
typedef typename AssocCont::mapped_type InMappedType;
typedef typename AssocCont::value_type InPairConstruct;
typedef typename AssocCont::hasher InHasher;
typedef typename AssocCont::key_equal InEq;
//...
}
//But now there is no external means of desginating the new container's hasher,equality functor etc...
//And as it turns out, I cant even instantiate a new return object from AssocCont<InKeyType,InMappedType> since it is a distinct and unknown type
AssocCont<InMappedType,InKeyType> outmap = AssocCont<InMappedType,InKeyType>(); // nope. equivalent to object<key,value><otherkey,othervalue>()
DOUBLE EDIT: in my haste to provide an example, I chose std::map as an alternate paramater example, which i realize doesn't have a hasher, nor five template arguments. So the basis of my question is still trying to diversify this function, but specifically for arguments that have five template arguments of their own with compatible behavior.... I have edited my post to mitigate this oversight.
I feel like this might be better implemented in a manner similar to the standard algorithms library. In other words, design your inversion function to take a range of iterators into the input container and an iterator into the output container. It will be simpler to implement and provide greater flexibility to users. Furthermore, it will be somewhat independent of the container type as long as the input and output iterators satisfy certain conditions (imposed by concepts perhaps). Here is an example which may not be exactly what you want but you can probably modify it to fit your needs:
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
namespace
{
template <class InputIt, class OutputIt>
void inverse_map(InputIt start, InputIt stop, OutputIt d_first)
{
while(start != stop)
{
*d_first = {start->second, start->first} ;
++d_first ;
++start ;
}
}
} // anonymous namespace
int main()
{
std::map<int, std::string> map_1 {{1, "foo"}, {2, "bar"}, {3, "foo"}} ;
std::unordered_map<std::string, int> map_2 ;
//
// Or, you can use:
//
// std::unordered_map<std::string, int, MyCustomHasher> map_2 ;
//
inverse_map(map_1.begin(), map_1.end(), std::inserter(map_2, map_2.end())) ;
for(const auto& [key, value]: map_2) // requires C++17
std::cout << key << ": " << value << "\n" ;
return 0;
}
Output:
bar: 2
foo: 1
Try it out online here.

Insert custom class with template into std::map

I have written my own class and I want to insert it into a map. See the example below:
#include <iostream>
#include <string>
#include <map>
#include <memory>
#include <mutex>
template <class T>
class A {
public:
T a;
A() = default;
~A() = default;
A(T i) { a = i; }
};
int main()
{
std::pair<int,A<int>> p;
p = std::make_pair<int,A<int>>(9,A<int>(1));
std::map<int, A<int>> m;
m.emplace(1,A<int>(1));
}
When I try to compile this, I get an enormous error. Please help interpret it. :)
See error here:
http://cpp.sh/9nc35
EDIT:
I had the typo, thanks! Though, the other problem I was struggling with first arose now. Seems like it is because of the mutex? Why?
Your map is defined as:
std::map<int, std::unique_ptr<A<int>>>
But in the next line you're trying to pass an std::pair<int, A<int>> to m.emplace() as the key.
I think you just want to do:
m.emplace(9, std::make_unique<A<int>>(1));
// ^
// Not `p`
You are trying to insert/emplace pair (pair<...>, A) into your map, while you've specified it's key as an int. You most-likely want m.emplace(9,std::make_unique<A<int>>(1)); (see 9 instead of p) or just m.insert(p); (would work fine in your cpp.sh).
Furthermore, your use of unique_ptr here is most-likely wrong/unwarranted and only complicates things. See fixed up example here: http://cpp.sh/3d2hw
Also, you may study STL collections/see some basic examples over at https://en.cppreference.com (https://en.cppreference.com/w/cpp/container/map/map for some map construction examples).

Is there a C++ container for unique values that supports strict size checking?

I'm looking for a C++ container to store pointers to objects which also meets the following requirements.
A container that keeps the order of elements (sequence container, so std::set is not suitable)
A container that has a member function which return the actual size (As std::array::size() always returns the fixed size, std::array is not suitable)
A container that supports random accesses such as operator [].
This is my code snippet and I'd like to remove the assertions used for checking size and uniqueness of elements.
#include <vector>
#include <set>
#include "assert.h"
class Foo {
public:
void DoSomething() {
}
};
int main() {
// a variable used to check whether a container is properly assigned
const uint8_t size_ = 2;
Foo foo1;
Foo foo2;
// Needs a kind of sequential containers to keep the order
// used std::vector instead of std::array to use member function size()
const std::vector<Foo*> vec = {
&foo1,
&foo2
};
std::set<Foo*> set_(vec.begin(), vec.end());
assert(vec.size() == size_); // size checking against pre-defined value
assert(vec.size() == set_.size()); // check for elements uniqueness
// Needs to access elements using [] operator
for (auto i = 0; i < size_; i++) {
vec[i]->DoSomething();
}
return 0;
}
Is there a C++ container which doesn't need two assertions used in my code snippet? Or should I need to make my own class which encapsulates one of STL containers?
So a class that acts like a vector except if you insert, it rejects duplicates like a set or a map.
One option might be the Boost.Bimap with indices of T* and sequence_index.
Your vector-like indexing would be via the sequence_index. You might even be willing to live with holes in the sequence after an element is erased.
Sticking with STLyou could implement a bidirectional map using 2 maps, or the following uses a map and a vector:
Note that by inheriting from vector I get all the vector methods for free, but I also risk the user downcasting to the vector.
One way round that without remodelling with a wrapper (a la queue vs list) is to make it protected inheritance and then explicitly using all the methods back to public. This is actually safer as it ensures you haven't inadvertently left some vector modification method live that would take the two containers out of step.
Note also that you would need to roll your own initializer_list constructor if you wanted one to filter out any duplicates. And you would have to do a bit of work to get this thread-safe.
template <class T>
class uniqvec : public std::vector<T*>
{
private:
typedef typename std::vector<T*> Base;
enum {push_back, pop_back, emplace_back, emplace}; //add anything else you don't like from vector
std::map <T*, size_t> uniquifier;
public:
std::pair<typename Base::iterator, bool> insert(T* t)
{
auto rv1 = uniquifier.insert(std::make_pair(t, Base::size()));
if (rv1.second)
{
Base::push_back(t);
}
return std::make_pair(Base::begin()+rv1.first.second, rv1.second);
}
void erase(T* t)
{
auto found = uniquifier.find(t);
if (found != uniquifier.end())
{
auto index = found->second;
uniquifier.erase(found);
Base::erase(Base::begin()+index);
for (auto& u : uniquifier)
if (u.second > index)
u.second--;
}
}
// Note that c++11 returns the next safe iterator,
// but I don't know if that should be in vector order or set order.
void erase(typename Base::iterator i)
{
return erase(*i);
}
};
As others have mentioned, your particular questions seems like the XY problem (you are down in the weeds about a particular solution instead of focusing on the original problem). There was an extremely useful flowchart provided here a number of years ago (credit to #MikaelPersson) that will help you choose a particular STL container to best fit your needs. You can find the original question here In which scenario do I use a particular STL container?.

How to create iterator without type names?

For example I have a std::map<int, string> _m; and I want to iterate through it.
So I have to write something like this: std::map<int, string>::iterator it = _m.begin().
The question is whether it is possible to create that iterator without using type names. Compiler knows the type of _m at the moment of creation of iterator, so why do I need to write those types myself?
UPDATE
I forgot to say that I've to use old C++ standard.
You're after the auto keyword:
auto it = _m.begin();
Compiler knows the type of _m at the moment of creation of iterator, so why do I need to write those types myself?
Yes, this is the exact logic that lead to the introduction of auto.
If you cannot use C++11, you're stuck with things like typedef. The whole reason the new standard introduced auto is because of the exact problem you're facing - it's awkward to have to manually write out long type names all the time.
typedef std::map<int, string>::iterator map_itr;
map_itr it = _m.begin();
Since you specify that you're locked back in time to C++98, then about the best you have is whatever black magic is behind BOOST_AUTO.
#include <boost/typeof/typeof.hpp>
#include <map>
#include <string>
int main()
{
std::map<int, std::string> m;
BOOST_AUTO(it, m.begin());
}
BOOST_FOREACH was created as a similar thing for dealing with loops without having to specify iterator types.
#include <boost/foreach.hpp>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
// populate v...
BOOST_FOREACH(int const &i, v)
{
std::cout << i << "\n";
}
}
Actually it is possible by using template function:
template <typename ITERATORTYPE> void myfunc(ITERATORTYPE it) {
//.. do whatever you want with your it
}
Then just invoke the template:
myfunc(_m.begin());
If you're stuck to C++03, there's no way to refer to the type without spelling it out if you really need it. The best you could do is introduce a typedef (or more) so that you only have to type the long type once:
typedef std::map<int, string> TypeOfM;
typedef TypeOfM::iterator MIterator;
TypeOfM _m;
However, when you use standard library algorithms, you don't have to name the iterators explicitly:
std::for_each(_m.begin(), _m.end(), /* a functor */);
If Boost is an option, and you really only want iteration, you could also use Boost.Foreach. Unfortunately, that would still need a typedef, because the value_type of map is a pair and thus contains a , which would trip up the preprocessor:
typedef std::pair<const int, string> Pair;
BOOST_FOREACH(const Pair &p, _m)
{
// Do whatever you need
}
Since you're trying to iterate over it, you may try one of the <algorithm>s such as std::for_each and pass a function
The best general way is by using C++11's auto keyword as type. In later versions this can be done even better with range-bsed loops without any type information.
for (it : _m) {
/* ... */
}
If you are stuck to old versions the only way to shorten the typename would be to create a template. This might make it more complex than writing the name, though. Something along those lines:
template <typename T>
void do_foo(T it, T end) {
for (; it < end; ++it) {
/* ... */
}
}
do_foo(_m.begin(), _m.end());
If wrapping your algorithm isn't the way you can also use a typedef to shorten it, but then you still have to type the name once.

Initialize size of vector<list<DATA> > and accesses

Hey I'm a little confusd about how the constructor initializes the size of the vector >.
This is my hpp
#include <vector>
#include <list>
#include <ostream>
using namespace std ;
typedef struct { double successful[2] , unsuccessful[2] ; } Perform ;
template <class DATA>
class Table {
private :
vector<list<DATA> > theList;
typename list<DATA>::iterator itr;
unsigned listSize;
unsigned actualSize;
unsigned probe;
...
and in my cpp
template <class DATA> Table<DATA>::Table(unsigned int size)
{
listSize = size;
actualSize = 0;
probe = 0;
theList(size); //Not sure how to make the vector of size "size"
}
and if I wanted to clear the lists in each vector location could I do a for loop and have the code be
theList[i].clear();
or would i have to do something different?
One last question is for inserting something into each of the lists in side the vectors, can I do
theList[i].push_back(data);
if not how do I do it?
Thanks for the help.
Congratulations, you have discovered the difference between initialization and assignment.
Assignment occurs when you try to set an existing object to a new value; initialization is when an object is created and its constructor is called.
What you're doing in your constructor (which, for some reason, isn't shown in your header, making your code more confusing than it has to be) is assignment. By the time the constructor body executes, all members have been initialized.
To initialize a member, use the initializer list syntax:
template <class DATA> Table<DATA>::Table(unsigned int size)
: listSize(size), actualSize(0), probe(0), theList(size)
{
}
This will call the constructor for each of the specified elements, with the specified arguments.
To set initial size you can use initialer list,
template <class DATA> Table<DATA>::Table(unsigned int size)
: theList(size)
{
...
or by calling resize method inside:
theList.resize(size);
For your first question, the code is right. This will create a vector with size elements, these elements will be default-constructed, which in ths case means they're empty lists. BUT you should place this initialization in the initialization list as follows (you should also do this for the other members btw).
template <class DATA> Table<DATA>::Table(unsigned int size) : theList(size)
{
...
}
This will prohibit the constructor from first calling the default constructor of your theList object (so it is a valid object).
The answer to your other questions is yes, but if you use C++11 you can probably save some tedious for-loops by using for_each and lambdas, for example like this:
std::for_each(theList.begin(),theList.end(),[&data](std::list& l) { l.push_back(data)});