I'm trying to make a wrapper to the STL map container, in order to add a const method to return the value given the key. In map, operator[] isn't const, and find() requires dereferencing to get the value (map.find()->second). I'm basing some of my "research" off of Idiomatic C++ for reading from a const map by the way.
The code so far (all inside a single header file):
#include <map>
template <typename K, typename V>
class easymap : public std::map<K, V>
{
//Constructor
easymap() : std::map<K, V>() {};
//The get method
V get(K key)
{
std::map<K, V>::const_iterator iter(find(key));
return iter != end() ? iter->second : V();
}
};
When I try to compile this, I get the following errors:
In member function `V easymap::get(K)':
expected `;' before "iter"
`iter' was not declared in this scope
there are no arguments to `end' that depend on a template parameter, so a declaration of `end' must be available|
(if you use `-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
Does how I'm trying to go about doing this make sense? If so, how do I make this work? If not, how would I go about achieving the effect I'm looking for?
Do not derive from std::map. Rather, wrap a std::map instance in your easymap, following the composition before inheritance principle. Besides of all the technical reasons, this reflects the design intent much better: provide a simplified API to map hiding the default one:
template<typename K, typename V>
class easymap {
std::map<K, V> mMap;
public:
V Get(K Key) const {
// ...
}
};
You are missing the template parameters for map, you have to specify typename when declaring the iterator (see here), and for some reason unknown to me (probably a namespace conflict) you have to use this when calling end():
template <typename K, typename V>
class easymap : public std::map<K,V>
{
//Constructor
easymap() : std::map<K, V>() {};
//The get method
V get(K key)
{
typename std::map<K, V>::const_iterator iter(find(key));
return iter != this->end() ? iter->second : V();
}
};
It's no a good idea to use STL container as base class. You should have a really good reason to do that and to be very careful.
The reason is, that none of the STL containers have a virtual destructor. So, if you have a pointer, for example std::map<..> *, that points to your object (that has inherited the map), one of the destructors will not be called. This is a 100% memory leak.
Related question to this one is: Is it okay to inherit implementation from STL containers, rather than delegate?
Related
Reading other topic here I've already wrote code detecting if class is associative container[1]. Right now in order to use equal_range I need to detect if it's normal map or multimap. Is there any way I can achieve my goal?
[1] Disambiguate template specialization between map-like and vector-like containers
You might probably add your own type trait:
template<typename>
struct is_map : std::false_type {};
template<typename K, typename V>
struct is_map<std::map<K, V>> : std::true_type {};
WANDBOX example
You could also use the suggestions in this post to specialise based on the existence of at() or operator[]
The original answer works perfectly well for checking if a type is a map or not. However, it doesn't work for testing if a type inherits from a map. I made various attempts at a general solution and finally I came up with the following code:
namespace details {
constexpr bool is_map(const void*) {
return false;
}
template<typename K, typename V, typename Comp, typename Alloc>
constexpr bool is_map(const std::map<K, V, Comp, Alloc>*) {
return true;
}
}
template<typename T>
constexpr bool is_map_v = details::is_map(static_cast<const T*>(nullptr));
using map1 = std::map<int, int>;
static_assert(is_map_v<map1>);
struct MyMap : public std::map<int, int>{};
static_assert(is_map_v<MyMap>);
static_assert(is_map_v<int> == false);
It has 2 overloads for the is_map function. One matches pointers to maps, and the other matches everything else. Because they take pointers, passing the address of something publicly derived from map will also be accepted.
The is_map(const void *) will only match if T can't possibly be a map.
I am interested in implementing a Java-collection-like environment for C++.
I know this isn't a good idea and so on but I don't really want to use it later, but just learn how to do some advanced OOP.
My problem is I want a base class template collection<T> with purely virtual functions. One of these functions should be map() which takes a std::function<R(T)>. Since map() should be virtual I don't know which return type I should use for it. collection<R> isn't possible because member function templates can't be virtual.
How can I add such map() member function for my collection<T> interface?
How can I add such map member function for my collection<T> interface?
The short answer is: you don't. If I have some collection<int> and I want to map std::to_string onto it, I need to produce a collection<std::string>. But a vector_collection<int> needs to produce a vector_collection<std::string> and a list_collection<int> needs to produce a list_collection<std::string> - so that type transformation itself needs to be virtualized. But you can't have virtual member function templates, so there's no way to express this.
In order for this to work, you would have to have a common base type for all of the objects you're putting in your container and then just have a common facade that you could cast between. That is, you really only have collection<unique_ptr<Object>> where map just gives you a different collection<unique_ptr<Object>>, and you just map your collection_facade<int, collection<unique_ptr<Object>>> into a collection_facade<std::string, collection<unique_ptr<Object>>>. With a lot of work and complete disregard for performance and type safety, you could get there.
This is the advantage of templates. If I want to write map for something like vector, I can just write that:
template <class T, class A, class F, class R = std::result_of_t<F(T)>>
std::vector<R, A> map(std::vector<T, A> const& v, F f) {
std::vector<R, A> mapped;
mapped.reserve(v.size());
for (T const& elem : v) {
mapped.push_back(f(elem));
}
return mapped;
}
or:
template <class T, class A, class F, class R = std::result_of_t<F(T)>>
std::vector<R, A> map(std::vector<T, A> const& v, F f) {
return std::vector<R, A>(
boost::make_transform_iterator(v.begin(), f),
boost::make_transform_iterator(v.end(), f)
);
}
I have to implement map() for each container separately - but I would have to do that anyway. And now I'm not giving anything up. Besides, how often are you writing algorithms that are runtime-container-agnostic?
Implement map as an external template function.
For instance, you can decompose map in two stages, internal virtual producer and external templated consumer.
template<typename T> struct Collection {
// virtual T next(); // Java way
// C++ way
// In simplest cases you can rely on iterator pairs.
struct const_iterator {
T const &operator*() const;
const_iterator &operator++();
}
virtual const_iterator begin() const;
virtual const_iterator end() const;
};
template<typename R, typename T> Collection<R> map(
Collection<T> const &coll, std::function<R(T)> const &f);
To implement essentially complicated containers and monadic compositions you can even deny begin() and end() and write an explicit (partial) template specialization.
I have a map visitor that's templated like this
template <class Map> class MyVisitor : public MyMapVisitor<Map>;
With Map being necessarily an std::map
And I want to have a method in it that I would like to create a list of all the members stored in my map as such:
std::vector <Map::*second_template_argument*> toList ();
Is there a way to do this? Because I'm not allowed to change the MyMapVisitor superclass but I can change the MyVisitor subclass.
For a std::map you have three options:
std::map::value_type, which is a key/value pair
std::map::key_type for the key
std::map::mapped_type for the mapped value.
For the more general case, if the class isn't kind of enough to provide typedefs for its template types, you can use decltype (if you have C++11). For your map example, you could do this:
std::map<int, double> myMap;
typedef decltype(myMap.begin()->second) valueType; // double
std::vector<valueType> myVector;
Note that myMap.begin() is not actually called by this code; the decltype expression simply evaluates to whatever type myMap.begin()->second would have returned (which is double in this case).
Obviously it's nicer to rely on the typedefs when you have them!
No. What you're describing is the ability to pass not a type but a member of the class. That can be passed at run time but there's no way to pass the name of a member as a template parameter.
There are a few ways. The first is to make the implmenetation a specialization.
template <class MapType> class MyVisitor;
template <class T, class J>
class MyVisitor<Map<T,J>>: public MyMapVisitor<Map<T,J>>
{
std::vector<J> toList();
}
I personally don't like this one, but sometimes it is all you've got.
The second is to expose the type via a public typedef (the way vector does):
template <typename T, typename J>
struct MyMap
{
typedef T sometype;
typedef J someothertype;
}
And then
std::vector<Map::someothertype> toList();
This might be a little silly question, but I just have to ask it. I am trying to use the unordered_map class in C++, but instead of referencing it everytime as tr1::unordered_map, I would like to just use the keyword hashMap.I know that
typedef tr1::unordered_map<string, int> hashMap
works but that kind of fixes the datatype of the key and the value corresponding to the hashMap whereas I would like to have more like the following:
#define hashMap tr1::unordered_map
where I can just define the datatype of the key and the value depending on the the requirement, but this does not work. Did anyone face this problem before?
Thanks
This is something that was missing from C++ before C++11. In C++11, you can use template using:
template<typename Key, typename Value>
using hashMap = tr1::unordered_map<Key, Value>;
A usual workaround for C++03 is to make a template structure with a type member:
template<typename Key, typename Value>
struct hashMap {
typedef tr1::unordered_map<Key, Value> type;
};
// then:
hashMap<string, int>::type myMap;
Inheriting from the class is possible in theory, but usually users refrain from doing so since the STL classes weren't meant to be inherited from.
One possibility would be to use inheritance to forward the key/value pair to unordered_map through a templated hashMap derrived class. IE:
template<typename key, typename value>
class hashMap : public tr1::unordered_map<key, value>
{
public:
// Add constructors to forward to tr1::unordered_map's constructors as
// needed
hashMap() : tr1::unordered_map<key, value>() {}
//...
};
Then you can use hashMap as a template but actually be using unordered_map's public interface.
hashMap<string, int> foo;
foo["bar"] = 5;
Just don't do anything fancy other than forward, as STL types don't have virtual destructors.
I am making a program in which I am inheriting publicly my Set class from a built-in STL container class set. I have to use the iterator type, while making some other specialized functions for my own Set class, as defined in the stl set class.
Now my question is: What would be the syntax to declare variables of iterator type inside my member functions? I have been trying:
template<typename T>
class Set : public set<T> {
public:
bool func()
{
iterator it,it2;
}
};
But the compiler is not recognizing iterator type. Kindly tell me the syntax to use the iterator type from the stl set class.
The compiler complains because it doesn't know that iterator is in fact a member of set<T>. set<T> is what's called a dependent type, because it depends on the type parameter T. In a nutshell, the compiler does not look inside dependent names when resolving types.
This FAQ entry is relevant to this question. Also, be sure to read through the answers to this Stack Overflow question. To fix it, use typename.
template<typename T>
class Set : public set<T> {
public:
bool func()
{
typename set<T>::iterator it;
typename set<T>::iterator it2;
}
};
But really, you should be using composition instead of inheritnace for this case, like this:
template<typename T>
class Set
{
public:
bool func()
{
typename set<T>::iterator it;
typename set<T>::iterator it2;
}
private:
std::set<T> set;
};
You should never ever ever ever ever inherit from an STL container class because the destructors of those classes are not virtual, and so every time you instantiate an instance of your derived class, you'll have a memory leak.
As In Silico suggested in one of his comments, give Set a member variable set instead of inheriting from it.