Today I created a map, where the value type has no default constructor. I was surprised that I could not use operator[] to insert the elements to this map, but I had to use the insert method.
So, what exactly are requirements for the key and value types for std::map?
Here is short example :
#include <map>
struct A
{
A(int){}
};
int main()
{
std::map< int, A > m;
A a1(2);
A a2(3);
A a3(4);
m[5] = a1;
m[3] = a2;
m[2] = a3;
}
I am compiling like this :
[vladimir#sandbox tmp]$ g++ b5.cpp -Wall -Wextra -ansi -pedantic
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stl_map.h: In member function ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = A, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, A> >]’:
b5.cpp:14: instantiated from here
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stl_map.h:419: error: no matching function for call to ‘A::A()’
b5.cpp:5: note: candidates are: A::A(int)
b5.cpp:4: note: A::A(const A&)
operator[] does indeed require default-constructibility because the semantics of this method mandate that if the key doesn’t yet exist, an appropriate entry is created. Thus:
map<TKey, TValue> mymap;
TKey foo = …;
TValue& x = mymap[foo];
will create and store a new object TValue() if foo doesn’t exist in the map, and return a reference to it.
This site make a great STL reference: http://www.sgi.com/tech/stl/
Basically, it says that map takes has mandatory 2 type arguments, Key and Data. Data needs to be Assignable, as Daniel said. Key, however, is stated to need to be a type that can be used with the type Compare, i.e. Compare designates a function object whose parameters are of type Key. In this case, the default Compare function object is std::less<T>, which is a Strict Weak Ordering that compares objects of type T using the operator<. Hence, if you do not change the Compare type, i.e. use the default, std::less<T> will be used with type Key, and thus operator< will be used with type Key, and thus Key needs to be comparable with operator<.
Hope that helps! I know it's a bit gratuitous and I don't mean to be condescending, but I just want to make sure it's absolutely clear how to go about reasoning about this.
Related
I am writing a program that passes around a struct type that I don't want to be modified. This struct has two const members, and looks as follows:
struct system_s {
std::string name;
std::string pkg;
char *const start_cmd[10];
char *const end_cmd[10];
bool ros;
bool equals(const system_s &cmp);
};
The struct is being stored in a map with the following format. It is a class member:
std::map<std::string, system_s> sys_map;
There is another temporary map. Think of sys_map as a cache if you prefer. But really you don't have to worry about how it is being used for the sake of this question. sys_map is being called to add a system to the temporary map as follows. It is in a class method:
add_system(sys_map[msg->system]); (*)
This function has the following definition. It is a class method:
int add_system(const system_s &sys);
When (*) is called, I get the following error:
system.h: In instantiation of ?std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = system_s; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, system_s> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = system_s; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::basic_string<char>]?:
/tc_manager_node.cpp:74:41: required from here
/system.h:26:8: error: uninitialized member ?system_s::start_cmd? with ?const? type ?char* const [10]? [-fpermissive]
struct system_s {
^
system.h:26:8: error: uninitialized member ?system_s::end_cmd? with ?const? type ?char* const [10]? [-fpermissive]
In file included from /usr/include/c++/4.8/map:61:0,
from /opt/ros/indigo/include/ros/console.h:42,
from /opt/ros/indigo/include/ros/ros.h:40,
from
/tc_manager_node.cpp:2:
/usr/include/c++/4.8/bits/stl_map.h:469:59: note: synthesized method ?system_s::system_s()? first required here
__i = insert(__i, value_type(__k, mapped_type()));
Why is this member of type system_s 'uninitialized'? It presumably stored already initialized in sys_map. Does it have something to do with passing the system_s as a reference in int add_system(const system_s &sys)?
The operator[] of map (which you invoke with sys_map[msg->system]) has the possibility of creating a new entry if the map entry is not found. The new entry is default-constructed, but your class is not default-constructible.
To fix this, don't use [] on the map. Instead use find to find the entry you are looking for.
As #Greg Kikola said, const members must be initialized. Check here on how to do that with initializer lists (Not to be confused with std::initializer_list): http://en.cppreference.com/w/cpp/language/initializer_list
The position of the const with pointers can sometimes be confusing. X * const p indicates:
“p is a const pointer to an X that is non-const”: you can’t change the pointer p itself, but you can change the X object via p. [source]
This means that the address a system_s is created with can never be changed. Which is bad since you're not constructor-initializing start_cmd or end_cmd this means that none of the 10 pointers can be assigned a valid address. They start with an uninitialized addresses and can never be assigned anything else.
EDIT:
This post is tagged: c++03. There is no straight forward way to initialize arrays in C++03. You can look at this question for some workarounds: Initializing a member array in constructor initializer If you have the ability to go with c++11 you can use List Initialization.
I'm trying to implement a priority queue which uses an object which has a const member that is used to define the priority of objects in the queue. The following is a stripped down version of what I'm using
#include <vector>
#include <queue>
class Event {
public:
Event(float _time) : time(_time) {};
const float time;
};
struct EventComp {
public:
bool operator()(const Event& a, const Event& b) const {
return a.time < b.time;
}
};
class EventQueue {
private:
std::priority_queue<Event, std::vector<Event>, EventComp> events;
};
int main(int argc, char *argv[])
{
EventQueue q;
}
When I try to compile (using g++ -std=c++11), I get the following error messages, which I don't quite understand.
g++ -std=c++11 events.cpp -o events
In file included from /usr/include/c++/4.8/queue:62:0,
from events.cpp:2:
/usr/include/c++/4.8/bits/stl_heap.h: In instantiation of ‘void std::__adjust_heap(_RandomAccessIterator, _Distance, _Distance, _Tp, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Event*, std::vector<Event> >; _Distance = long int; _Tp = Event; _Compare = EventComp]’:
/usr/include/c++/4.8/bits/stl_heap.h:448:15: required from ‘void std::make_heap(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Event*, std::vector<Event> >; _Compare = EventComp]’
/usr/include/c++/4.8/bits/stl_queue.h:411:48: required from ‘std::priority_queue<_Tp, _Sequence, _Compare>::priority_queue(const _Compare&, _Sequence&&) [with _Tp = Event; _Sequence = std::vector<Event>; _Compare = EventComp]’
events.cpp:15:7: required from here
/usr/include/c++/4.8/bits/stl_heap.h:315:29: error: use of deleted function ‘Event& Event::operator=(Event&&)’
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __secondChild));
^
events.cpp:4:7: note: ‘Event& Event::operator=(Event&&)’ is implicitly deleted because the default definition would be ill-formed:
class Event {
^
events.cpp:4:7: error: non-static const member ‘const float Event::time’, can’t use default assignment operator
In file included from /usr/include/c++/4.8/queue:62:0,
from events.cpp:2:
/usr/include/c++/4.8/bits/stl_heap.h:321:29: error: use of deleted function ‘Event& Event::operator=(Event&&)’
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first
^
I assume that some part of the internal structure of the priority_queue needs the move assignment operator to insert elements, but the const member seems to prevent that operator from being defined. I tried using the rule of five, but it didn't seem to work out. What do I need to add to the class definitions to get this working?
edit: I'm aware that I can remove the const qualifier from the member variable, make it private, and add an accessor member function to get the value of the variable, but I would rather have it public and const, so I'm interested in solutions which keep the member variable as it is in the example (if that is even possible).
The default constructor of std::priority_queue<Event, ...> requires Event to be MoveAssignable because it calls std::make_heap (which has this requirement). Event is not MoveAssignable because of the const float time data member. If you remove the const qualifier from this data member, your code should compile.
I'm aware that I can remove the const qualifier from the member
variable, make it private, and add an accessor member function to
get the value of the variable, but I would rather have it public and
const, so I'm interested in solutions which keep the member variable
as it is in the example (if that is even possible).
Ultimately, to use this std::lib facility, Event will have to be MoveAssignable. This requirement can be met with either a move assignment operator, or a copy assignment operator. Either or both of these can be supplied by you, or the compiler. But when you have a const data member, the compiler supplied special member will be implicitly deleted.
Go ahead and supply your own copy and/or move assignment operator if you desire:
class Event {
public:
Event(float _time) : time(_time) {};
const float time;
Event(Event&&) = default;
Event& operator=(Event&&); // Supply this
};
I do not have a good guess as to what you might want the move (or copy) assignment operator to do, since you have stated that once constructed, you do not want time to ever change. That is a design decision which you must work out (and has more to do with software design in general, than C++ syntax).
<aside>
In the comments of the question POW notes that the code in the question compiles with clang (more accurately, with libc++). The reason for this is a nearly conforming extension in libc++:
The standard specifies this priority_queue constructor:
explicit priority_queue(const Compare& x = Compare(),
Container&& c = Container());
which calls std::make_heap with the container c. But the standard also gives leeway to implementors to replace member function signatures with default values, with equivalent overloads. libc++ replaces this single signature with the following three:
priority_queue();
explicit priority_queue(const Compare& x);
explicit priority_queue(const Compare& x, Container&& c);
And only the 3rd needs to call std::make_heap. Thus the default constructor in libc++ places fewer requirements on the value_type.
I said nearly conforming. The part that is not completely conforming is that the default constructor is not declared explicit. This was a conscious design decision on the part of the implementor to make priority_queue easier to use.
</aside>
In the past I have used both templates and dynamic binding in C++, however recently I attempted to use them together and found that it was impossible to compile.
What I am trying to do is something like this:
std::map<MyClass, unsigned int> mymap;
Where MyClass happens to be a class utilizing dynamic memory binding. After a few hours of searching I am given the impression that this causes implications which I still can't resolve, so I was hoping for some guidance on the issue and how it can be resolved.
My final goal is to be able to do something like this:
std::vector<MyClass> MyClassPool;
//fill the vector with MyClass objects...
for(usigned int i=0 ; i<MyClassPool.size() ; i++)
{
mymap[ MyClassPool[i] ] = i;
}
I have tried using pointers in various ways but it's not working and I fail to see what can be done.
I get the following errors with the above:
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h: In member function `bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Instance]':
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_map.h:338: instantiated from `_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = Instance, _Tp = float, _Compare = std::less<Instance>, _Alloc = std::allocator<std::pair<const Instance, float> >]'
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_function.h:227: error: no match for 'operator<' in '__x < __y'
That compile error means you have no operator < defined for Instance. map needs to be able to sort keys and needs this function. If you'd rather not define operator <, you need to provide a comparison function as a third template parameter for map, i.e. std::map<Instance, float, compare_instances>.
... Come to think of it, you're sure you want Instance as the key and float as the data, and not the other way around? I.e. you're searching in the map for an Instance to get a float in return?
You don't provide operator< for MyClass. It is required by std::map. You have two options: provide a comparator as the third template argument to map OR implement the operator in MyClass.
This has nothing to do with "dynamic binding" (which is not what is meant here anyway). Your class needs to have an order to be put into a map. It needs operator<.
I'm using the latest available GCC build from repository. I decided to use it because some additional C++0x features. However now I stuck with something what supposes to work - I want to add new element to map via r-value. Simplified code, which demonstrates problem:
#include <tr1/unordered_map>
class X
{
public:
X (void) { /* ... */ };
X (const X& x) = delete;
X (X&& x) { /* ... */ };
};
int main (void)
{
std::tr1::unordered_map<int, X> map;
// using std::tr1::unordered_map<int, X>::value_type didn't help too
std::pair<int, X> value (1, X ());
map.insert (std::move (value));
}
Notice, that when replace X class with some primitive type like int code compiles and work just fine.
In my production code class corresponding to X doesn't have copy constructor too.
Error message is (like all template-related errors) long and unreadable and I'm not sure if it's good idea to put it here. Notify me if you want error message, so I'll update this question. Last part of message is interesting:
(...)
/usr/include/c++/trunk/ext/new_allocator.h:106:9: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int, _T2 = X, std::pair<_T1, _T2> = std::pair<const int, X>]’
In file included from /usr/include/c++/trunk/utility:71:0,
from /usr/include/c++/trunk/tr1/unordered_map:34,
from kod.cpp:1:
/usr/include/c++/trunk/bits/stl_pair.h:110:17: error: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int, _T2 = X, std::pair<_T1, _T2> = std::pair<const int, X>]’ is implicitly deleted because the default definition would be ill-formed:
/usr/include/c++/trunk/bits/stl_pair.h:110:17: error: use of deleted function ‘X::X(const X&)’
Moreover this should work, because similar bug was already fixed [C++0x] Implement emplace* in associative and unordered containers.
Maybe I'm doing something wrong? I want to be sure, that's GCC or libstdc++ bug before reporting it.
Your code looks correct to me except for your use of tr1. tr1-qualified stuff doesn't know about rvalue reference or move semantics.
I took your code, removed tr1 from the header and namespace qualifiers, and successfully compiled your code using g++-4.4 and libc++ (http://libcxx.llvm.org/). Try removing tr1.
The value_type of that unordered_map is not std::pair<int, X>. It is std::pair<const int, X>. Maybe if you use that type for the value it will work better.
decltype(map)::value_type value(1, X());
map.insert(std::move(value));
Although I don't exactly see why your code shouldn't work as is.
The following code says that passing the map as const into the operator[] method discards qualifiers:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class MapWrapper {
public:
const int &get_value(const int &key) const {
return _map[key];
}
private:
map<int, int> _map;
};
int main() {
MapWrapper mw;
cout << mw.get_value(42) << endl;
return 0;
}
Is this because of the possible allocation that occurs on the map access? Can no functions with map accesses be declared const?
MapWrapper.cpp:10: error: passing const std::map<int, int, std::less<int>,
std::allocator<std::pair<const int, int> > > as this argument of
_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&)
[with _Key = int, _Tp = int, _Compare = std::less<int>,
_Alloc = std::allocator<std::pair<const int, int> >] discards qualifiers
std::map's operator [] is not declared as const, and cannot be due to its behavior:
T& operator[] (const Key& key)
Returns a reference to the value that is mapped to a key equivalent to key, performing insertion if such key does not already exist.
As a result, your function cannot be declared const, and use the map's operator[].
std::map's find() function allows you to look up a key without modifying the map.
find() returns an iterator, or const_iterator to an std::pair containing both the key (.first) and the value (.second).
In C++11, you could also use at() for std::map. If element doesn't exist the function throws a std::out_of_range exception, in contrast to operator [].
Since operator[] does not have a const-qualified overload, it cannot be safely used in a const-qualified function. This is probably because the current overload was built with the goal of both returning and setting key values.
Instead, you can use:
VALUE = map.find(KEY)->second;
or, in C++11, you can use the at() operator:
VALUE = map.at(KEY);
You cannot use operator[] on a map that is const as that method is not const as it allows you to modify the map (you can assign to _map[key]). Try using the find method instead.
Some newer versions of the GCC headers (4.1 and 4.2 on my machine) have non-standard member functions map::at() which are declared const and throw std::out_of_range if the key is not in the map.
const mapped_type& at(const key_type& __k) const
From a reference in the function's comment, it appears that this has been suggested as a new member function in the standard library.
First, you should not be using symbols beginning with _ because they are reserved to the language implementation/compiler writer. It would be very easy for _map to be a syntax error on someone's compiler, and you would have no one to blame but yourself.
If you want to use an underscore, put it at the end, not the beginning. You probably made this mistake because you saw some Microsoft code doing it. Remember, they write their own compiler, so they may be able to get away with it. Even so, it's a bad idea.
the operator [] not only returns a reference, it actually creates the entry in the map. So you aren't just getting a mapping, if there is none, you are creating one. That's not what you intended.