Here is the approximate code of my question. First, we have a struct with a constructor:
struct Pair{
Pair(int a, int b): (first (a) , second (b) ) {}
int first;
int second;
};
which is used in a map
map<string, Pair> mymap;
I would like to initialize this map in a function
void f(map<string, Pair>* mymap, string c,int x, int y )
{
(*mymap)[c]=Pair(x,y);
}
But the compiler says first that it could not find an appropriate constructor and then the next lines are that not enough arguments are provided for the constructor.
A friend of mine told me that I should write the function like this:
void f(map<string, Pair>& mymap, const string& c,int x, int y )
{
if (mymap.find(c) != mymap.end()) {
mymap[c] = Pair(x,y);
}
}
But he could not explain why Type& should be used here instead of Type *, and I would like to clarify this point. Can anybody explain?
The problem is that operator[] in a map requires that the value type is default constructible. If you don't want your Pair to be default constructible, you will have to avoid using operator[]:
void f(map<string, Pair>& mymap, string c,int x, int y )
{
mymap.insert( std::make_pair(c,Pair(x,y)) );
}
You may have misunderstood what your friend suggested. The problem with operator[] is not that it requires the default constructor if it needs to create a new element, but that it requires it in case it might need to. That is, whether the element exists before hand or not does not really matter.
If you also mean to update, then you need to consider also that option:
void f(map<string, Pair>& mymap, string c,int x, int y )
{
auto res = mymap.insert( std::make_pair(c,Pair(x,y)) );
if ( !res.second )
res.first->second = Pair(x,y);
}
Basically the insert operation returns a pair of the iterator pointing at the key and a bool indicating if this insert created the object or if it was already there (in which case the value in the map is unmodified). By storing the result, we can test and if the insert did not create the value, we can update it through the returned iterator.
Calling operator[] on map will require your type to be default constructible. You can avoid that by using map::insert or map::emplace
You will need a default contrructor:
Pair(): first () , second () {}
This is needed for the map's operator[], which creates a default-constructed mapped_type
when called with a non-existent key-
and a less-than operator that implements strict weak ordering:
struct Pair {
// as before
bool operator<(const Pair& rhs) const {
/ some code to implement less-than
}
};
or you can pass a comparison functor or function implementinf strict weak ordering as a
third template argument.
The standard containers need a default constructor. They will use the operator= to set the correct value at some point after construction.
Related
I find the update operation on std::set tedious since there's no such an API on cppreference. So what I currently do is something like this:
//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);
Basically the iterator return by Set is a const_iterator and you can't change its value directly.
Is there a better way to do this? Or maybe I should override std::set by creating my own (which I don't know exactly how it works..)
set returns const_iterators (the standard says set<T>::iterator is const, and that set<T>::const_iterator and set<T>::iterator may in fact be the same type - see 23.2.4/6 in n3000.pdf) because it is an ordered container. If it returned a regular iterator, you'd be allowed to change the items value out from under the container, potentially altering the ordering.
Your solution is the idiomatic way to alter items in a set.
C++17 introduced extract, see Barry's answer.
If you're stuck with an older version, there are 2 ways to do this, in the easy case:
You can use mutable on the variable that are not part of the key
You can split your class in a Key Value pair (and use a std::map)
Now, the question is for the tricky case: what happens when the update actually modifies the key part of the object ? Your approach works, though I admit it's tedious.
In C++17 you can do better with extract(), thanks to P0083:
// remove element from the set, but without needing
// to copy it or deallocate it
auto node = Set.extract(iterator);
// make changes to the value in place
node.value() = 42;
// reinsert it into the set, but again without needing
// to copy or allocate
Set.insert(std::move(node));
This will avoid an extra copy of your type and an extra allocation/deallocation, and will also work with move-only types.
You can also extract by key. If the key is absent, this will return an empty node:
auto node = Set.extract(key);
if (node) // alternatively, !node.empty()
{
node.value() = 42;
Set.insert(std::move(node));
}
Update: Although the following is true as of now, the behavior is considered a defect and will be changed in the upcoming version of the standard. How very sad.
There are several points that make your question rather confusing.
Functions can return values, classes can't. std::set is a class, and therefore cannot return anything.
If you can call s.erase(iter), then iter is not a const_iterator. erase requires a non-const iterator.
All member functions of std::set that return an iterator return a non-const iterator as long as the set is non-const as well.
You are allowed to change the value of an element of a set as long as the update doesn't change the order of elements. The following code compiles and works just fine.
#include <set>
int main()
{
std::set<int> s;
s.insert(10);
s.insert(20);
std::set<int>::iterator iter = s.find(20);
// OK
*iter = 30;
// error, the following changes the order of elements
// *iter = 0;
}
If your update changes the order of elements, then you have to erase and reinsert.
You may want to use an std::map instead. Use the portion of Element that affects the ordering the key, and put all of Element as the value. There will be some minor data duplication, but you will have easier (and possibly faster) updates.
I encountered the very same issue in C++11, where indeed ::std::set<T>::iterator is constant and thus does not allow to change its contents, even if we know the transformation will not affect the < invariant. You can get around this by wrapping ::std::set into a mutable_set type or write a wrapper for the content:
template <typename T>
struct MutableWrapper {
mutable T data;
MutableWrapper(T const& data) : data(data) {}
MutableWrapper(T&& data) : data(data) {}
MutableWrapper const& operator=(T const& data) { this->data = data; }
operator T&() const { return data; }
T* operator->() const { return &data; }
friend bool operator<(MutableWrapper const& a, MutableWrapper const& b) {
return a.data < b.data;
}
friend bool operator==(MutableWrapper const& a, MutableWrapper const& b) {
return a.data == b.data;
}
friend bool operator!=(MutableWrapper const& a, MutableWrapper const& b) {
return a.data != b.data;
}
};
I find this much simpler and it works in 90% the cases without the user even noticing there to be something between the set and the actual type.
This is faster in some cases:
std::pair<std::set<int>::iterator, bool> result = Set.insert(value);
if (!result.second) {
Set.erase(result.first);
Set.insert(value);
}
If the value is usually not already in the std::set then this can have better performance.
I have the following problem. I have a std::unordered_map that contains an object as the value. Now I want to modify an object that I previously inserted.
class Point
{
public:
Point(float _x, float _y) : x(_x), y(_y) {}
float x;
float y;
};
std::unordered_map<int, Point> points;
// ... add some values to it ...
points[1].x = 20.f; // error?
I get a weird long compile error about point not being able to be default constructed. The way I understand it operator [] returns a reference to the mapped type (aka the value), so why can't I modify it?
If the key isn't in the map, operator [] is required to create one. The expression
points[1]
needs to be able to default-insert a Point in case of lookup failure (regardless of whether lookup failure ever occurs - this is a compile-time requirement not a run-time check). That requirement cannot be satisfied by Point because Point is not default constructible. Hence the compile error. If you want to use unordered_map::operator[] , you'll need to add a default constructor.
If a default constructed Point doesn't make sense for your usage - then you simply cannot use operator[] and will have to use find throughout (or at() if you're okay with exceptions):
auto it = points.find(1);
if (it != points.end()) {
it->second.x = 20.f;
}
points.at(1).x = 20.f; // can throw
operator[] constructs an object of mapped type in-place if no element exists with the given key. In a map with a default allocator, operator[] requires the mapped type to be default constructible. More generally, the mapped type must be emplace constuctible.
The easy solution is to add a default constructor to your class.
Point() : Point(0.f, 0.f) {}
If this isn't possible, you will have to use other functions to access map elements.
To access an existing mapped object, you can using at, which will throw a std::out_of_range exception if no element exists with the given key.
points.at(1).x = 20.f;
Alternatively, you can use find, which returns an iterator to the element with the given key, or to the element following the last element in the map (see end) if no such element exists.
auto it = points.find(1);
if (it != points.end())
{
it->second = 20.f;
}
operator[] cannot be used on a map or unordered_map without the data being default-constructible. This is because if the object is not found, it will create it via default-construction.
The easy solution is to make your type default-constructible.
if not:
template<class M, class K, class F>
bool access_element( M& m, K const& k, F&& f ) {
auto it = m.find(k);
if (it == m.end())
return false;
std::forward<F>(f)(it->second);
return true;
}
then:
std::unordered_map<int, Point> points;
points.emplace(1, Point(10.f, 10.f));
access_element(points, 1, [](Point& elem){
elem.x = 20.f;
});
will do what points[1].x = 20.f; does without risking exception code or having to make Point default-constructible.
This pattern -- where we pass a function to mutate/access an element to a container -- is stealing a page from Haskell monad design. I would make it return optional<X> instead of bool, where X is the return type of the passed in function, but that is going a bit far.
I have a simple question. I have a class say A with a private member that is a stl container, for example a vector of ints. Is there a way to use its methods (e.g. size, resize, etc...) ? Does the classic "get function" suffice ?
Class A
{
private:
std::vector<int> v;
public:
std::vector<int> get_v() {return v;};
};
If yes, isn't a "get function" meant to only get the member and not to modify the member ?
Thank you very much
The normal thing to do here is to return a constant reference to the data member:
const std::vector<int>& get_v() const
{
return v;
}
Note that I've also made the function constant. This tells you that the function will not modify any data members in the class.
Currently, you are taking a deep copy of the vector, which is expensive in terms of performance and also detaches you from the original data.
If you want to call methods like resize (that change the vector) then you can also supply a non-constant version of the function (overloading on const) is allowed in C++):
std::vector<int>& get_v()
{
return v;
}
The compiler will call the const version if you have a const pointer (or reference) to an instance of A. Else if will call the non-const version.
That getter will return a copy of the internal vector. Any modifications you make will only affect the copy. That might be okay if you also provide a setter so that the modified copy can be passed back, so you could do something like this:
A a;
std::vector<int> v = a.get_v();
v.push_back(5);
a.set_v(v);
Alternatively, you can make the getter return a reference to the internal vector (so its return type would be std::vector<int>&, then you can modify it from outside.
Your get method returns a copy of the data member. So such a method does not allow to modify the original vector,
If you want to provide an access to elements of the vector you can either overload operator[] or write a get method with a parameter.
For example
Class A
{
private:
std::vector<int> v;
public:
int operator []( std::vector<int>::size_type n ) const { return v[n]; }
int & operator []( std::vector<int>::size_type n ) { return v[n]; }
};
or you can write get methods that do the same
Class A
{
private:
std::vector<int> v;
public:
int get( std::vector<int>::size_type n ) const { return v[n]; }
int & get( std::vector<int>::size_type n ) { return v[n]; }
};
As for the idea to use a copy of the vector or a reference to it that to get its size then it is not a good idea. It is better to provide a method that returns the size of the vector. In this case the user interface will not depend on the type of the container.
With template functions from <algorithm> you can do things like this
struct foo
{
int bar, baz;
};
struct bar_less
{
// compare foo with foo
bool operator()(const foo& lh, const foo& rh) const
{
return lh.bar < rh.bar;
}
template<typename T> // compare some T with foo
bool operator()(T lh, const foo& rh) const
{
return lh < rh.bar;
}
template<typename T> // compare foo with some T
bool operator()(const foo& lh, T rh) const
{
return lh.bar < rh;
}
};
int main()
{
foo foos[] = { {1, 2}, {2, 3}, {4, 5} };
bar_less cmp;
int bar_value = 2;
// find element {2, 3} using an int
auto it = std::lower_bound(begin(foos), end(foos), bar_value, cmp);
std::cout << it->baz;
}
In std::set methods like find you have to pass an object of type set::key_type which often forces you to create a dummy object.
set<foo> foos;
foo search_dummy = {2,3}; // don't need a full foo object;
auto it = foos.find(search_dummy);
It would be so helpful if one can call just foos.find(2). Is there any reason why find can't be a template, accepting everything that can be passed to the less predicate. And if it is just missing, why isn't it in C++11 (I think it isn't).
Edit
The main question is WHY isn't it possible and if it was posiible, WHY decided the standard not to provide it. A a second question you can propose workarounds :-) (boost::multi_index_container crosses my mind just now, which provides key extraction from value types)
Another Example with a more expensive to construct value type. The key name is part of the type and should not be used as a copy in maps key;
struct Person
{
std::string name;
std::string adress;
std::string phone, email, fax, stackoferflowNickname;
int age;
std::vector<Person*> friends;
std::vector<Relation> relations;
};
struct PersonOrder
{
// assume that the full name is an unique identifier
bool operator()(const Person& lh, const Person& rh) const
{
return lh.name < rh.name;
}
};
class PersonRepository
{
public:
const Person& FindPerson(const std::string& name) const
{
Person searchDummy; // ouch
searchDummy.name = name;
return FindPerson(searchDummy);
}
const Person& FindPerson(const Person& person) const;
private:
std::set<Person, PersonOrder> persons_;
// what i want to avoid
// std::map<std::string, Person> persons_;
// Person searchDummyForReuseButNotThreadSafe;
};
std::find_if works on an unsorted range. So you can pass any predicate you want.
std::set<T> always uses the Comparator template argument (std::less<T> by default) to maintain the order of the collection, as well as find elements again.
So if std::set::find was templated, it would have to require that you only pass a predicate that observes the comparator's total ordering.
Then again, std::lower_bound and all the other algorithms that work on sorted ranges already require exactly that, so that would not be a new or surprising requirement.
So, I guess it's just an oversight that there's no find_if() (say) on std::set. Propose it for C++17 :) (EDIT:: EASTL already has this, and they used a far better name than I did: find_as).
That said, you know that you shouldn't use std::set, do you? A sorted vector will be faster in most cases and allows you the flexibility you find lacking in std::set.
EDIT: As Nicol pointed out, there're implementations of this concept in Boost and Loki (as well as elsewhere, I'm sure), but seeing as you can't use their main advantage (the built-in find() method), you would not lose much by using a naked std::vector.
The standard states that std::set::find has logarithmic time complexity. In practice this is accomplished by implementing std::set as a binary search tree, with a strict weak ordering comparison used as sorting criteria. Any look-up that didn't satisfy the same strict weak ordering criteria wouldn't satisfy logarithmic complexity. So it is a design decision: if you want logarithmic complexity, use std::set::find. If you can trade complexity for flexibility, use std::find_if on the set.
They've provided for what you want, but in a rather different way than you're considering.
Actually, there are two different ways: one is to build a constructor for the contained class that 1) can be used implicitly, and 2) requires only the subset of elements that you really need for comparison. With this in place, you can do a search for foods.find(2);. You will end up creating a temporary object from 2, then finding that temporary object, but it will be a true temporary. Your code won't have to deal with it explicitly (anywhere).
Edit: What I'm talking about here would be creating an instance of the same type as you're storing in the map, but (possibly) leaving any field you're not using as a "key" un-initialized (or initialized to something saying "not present"). For example:
struct X {
int a; // will be treated as the key
std:::string data;
std::vector<int> more_data;
public:
X(int a) : a(a) {} // the "key-only" ctor
X(int a, std::string const &d, std::vector<int> const &m); // the normal ctor
};
std::set<X> s;
if (s.find(2)) { // will use X::X(int) to construct an `X`
// we've found what we were looking for
}
Yes, when you construct your X (or what I've called X, anyway) with the single-argument constructor, chances are that what you construct won't be usable for anything except searching.
end edit]
The second, for which the library provides more direct support is often a bit simpler: if you're really only using some subset of elements (perhaps only one) for searching, then you can create a std::map instead of std::set. With std::map, searching for an instance of whatever you've specified as the key type is supported explicitly/directly.
key_type is a member type defined in set containers as an alias of Key, which is the first template parameter and the type of the elements stored in the container.
See documentation.
For user-defined types there is no way for the library to know what the key type is. It so happens that for your particular use case the key type is int. If you use a set< int > s; you can call s.find( 2 );. However, you will need to help the compiler out if you want to search a set< foo > and want to pass in only an integer (think how will the set's ordering work between foo and an int).
Because if you want to do std::find(2) you'll have to define how int will compare with foo in addition to the comparison between two foos. But since int and foo are different types, you will need actually two additional functions:
bool operator<(int, foo);
bool operator<(foo, int);
(or some other logical equivalent pair).
Now, if you do that, you are actually defining a bijective function between int and foo and you could as well simply use a std::map<int, foo> and be done.
If you still don't want the std::map but you want the benefits of a sorted search, then the dummy object is actually the easiest way to go.
Granted, the standard std::set could provide a member function, to which you pass a function that receives a foo and return -1, 0, 1 if it is less, equal or greater than the searched one... but that's not the C++ way. And note that even bsearch() takes two arguments of the same type!
I'm writing a simple hash map class:
template <class K, class V> class HashMap;
The implementation is very orthodox: I have a heap array which doubles in size when it grows large. The array holds small vectors of key/value pairs.
Vector<Pair<K, V> > *buckets;
I would like to overload the subscript operator in such a way that code like this will work:
HashMap<int, int> m;
m[0] = 10; m[0] = 20;
m[2] = m[1] = m[0];
In particular,
For m[k] = v where m does not contain k, I'd like a new entry to be added.
For m[k] = v where m does contain k, I'd like the old value to be replaced.
In both of these cases, I'd like the assignment to return v.
Presumably the code will look something like
V& operator[](K &key)
{
if (contains(key))
{
// the easy case
// return a reference to the associated value
}
else
{
Vector<Pair<K, V> > *buck = buckets + hash(k) % num_buckets;
// unfinished
}
}
How should I handle the case where the key is not found? I would prefer to avoid copying values to the heap if I can.
I suppose I could make a helper class which overloads both the assignment operator and a cast to V, but surely there is a simpler solution?
Edit: I didn't realize that std::map required that the value type have a zero argument constructor. I guess I will just default-construct a value as well.
How should I handle the case where the key is not found?
Insert a new element with that key and return a reference to the value of that new element. Effectively, your pseudocode becomes something equivalent to:
if (!contains(key))
insert(Pair<K, V>(key, V()));
return reference_to_the_element_with_that_key;
This is exactly what your requirement is, too. You said "For m[k] = v where m does not contain k, I'd like a new entry to be added."
How should I handle the case where the key is not found?
std::map creates a new object, and inserts it into the map, and returns its reference. You can also do the same.
Alternatively, you can throw an exception KeyNotFoundException like the way .NET map throws. Of course, you've to define KeyNotFoundException yourself, possibly deriving from std::runtime_exception.
By the way, as a general rule, always implement operator[] in pair as:
V &operator[](const K &key);
const V &operator[](const K &key) const;
Just for the sake for const-correctness. However, if you decide to create a new object and insert it into the map, when the key is not found, then this rule is not applicable here, as const version wouldn't make sense in this situation.
See this FAQ:
What's the deal with "const-overloading"?
It sounds like what you want is a "smart reference", which you cannot generically implement in C++ because you cannot overload the dot operator (among other reasons).
In other words, instead of returning a reference to a V, you would return a "smart reference" to a V, which would contain a pointer to V. That smart reference would implement operator=(const V &v) as this->p = new V(v), which only requires a copy constructor (not a zero-argument constructor).
The problem is that the smart reference would have to behave like an actual reference in all other ways. I do not believe you can implement this in C++.
One not-quite-solution is to have your constructor take a "default" instance of V to use for initializing new entries. And it could default to V().
Like this:
template<class K, class V> class HashMap {
private:
V default_val;
public:
HashMap(const V& def = V()) : default_val(def) {
...
}
...
};
When V lacks a zero-argument constructor, HashMap h will not compile; the user will need to provide a V object whose value will be returned when a key is accessed for the first time.
This assumes V has a copy constructor, of course. But from your examples, that seems like a requirement anyway.
The simple solution is to do as std::map does: construct a new entry,
using the default constructor of the value type. This has two
drawbacks: you won't be able to use [] on a HashMap const, and you
can't instantiate HashMap with a value type which doesn't have a default
constructor. The first is more or less implicit in the specification,
which says that [] may modify the map. There are several solutions
for the second: the simplest is probably to pass an instance of a
"default" value to the constructor, which saves it, and uses it to copy
construct the new instance, e.g.:
template <typename Key, typename Value>
class HashMap
{
// ...
Value m_defaultValue;
public:
HashMap( ..., Value const& defaultValue = Value() )
: ... , m_defaultValue( defaultValue )...
Value& operator[]( Key& key )
{
// ...
// not found
insert( key, m_defaultValue );
// return reference to newly inserted value.
}
};
Alternatively, you can have operator[] return a proxy, something like:
template <typename Key, typename Value>
class HashMap::Helper // Member class of HashMap
{
HashMap* m_owner;
Key m_key;
public:
operator Value&() const
{
if ( ! m_owner->contains( m_key ) )
m_owner->createEntryWithDefaultValue( m_key );
return m_owner->getValue( m_key );
}
Helper& operator=( Value const& newValue ) const
{
m_owner->insert( m_key, newValue );
return *this;
}
};
Note that you'll still need the default value for the case where someone
writes:
v = m[x];
and x isn't present in the map. And that things like:
m[x].f();
won't work. You can only copy the entire value out or assign to it.
(Given this, I'd rather prefer the first solution in this case. There
are other cases, however, where the proxy is the only solution, and we
have to live with it.)