unordered_map with referenced key - c++

I have something wrong with the new C++ unordered_map: I would like to use the operator[] with a const key, but I get rejected.
I cannot give the whole code, but I can simplify my problem like this:
#include <unordered_map>
class A {
public:
A();
};
class B {
public:
B();
};
int main(int argc, char **argv) {
std::unordered_map<A &, B> myMap;
A a;
const A &ar = a;
B b;
myMap[ar] = b;
}
The output of the compiler is a bit long, but ends with:
/usr/include/c++/4.6/bits/hashtable_policy.h:537:5: note: no known conversion for argument 1 from ‘const A’ to ‘A&’
I use a const A & because, in my code, some method give it to me as is. And, by the way, the key should be const. I have tried a std::unordered_map<const A &, B> myMap; instead, but it does not work either.
I use gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5), with the -std=c++0x flag.
Could you please tell me why this is forbidden? I must say I do not understand the reason.
Many thanks (and please excuse me if the question is stupid...).

The reason is that operator[] is specified as follows (note that the same holds for just std::map):
Value& operator[](Key const& k);
In your case, Key is A&, so this expands to:
B& operator[](A& const& k);
And since references-to-references are invalid and the top-level reference is dropped when created through typedefs or template parameters, you get just:
B& operator[](A&);
Which can't handle an A const& argument.
In general, I'd advise against using a mutable reference as the key, since mutable keys are a good source for errors.

Using a reference as a key is a Bad Idea: It is bound to cause problems because the life-time of the map and the keys aren't aligned. The key type of your map of references is T& (well, there is a const added at the end but it would be illegal). Trying to bind a T const& to a T& doesn't work. Hence, you can't use T const& to look-up in a map using T& as a key.
There are other things which won't work. You should not try to use a map with a key of T& (or T* for that matter): use values as keys!

Related

How is std::map implemented so it can require its key_type to be comparable?

This is my implementation of the Box class:
class Box {
friend ostream& operator<<(ostream &os, const Box &b);
friend bool operator<(const Box &left, const Box &right);
public:
Box(int i, double d);
~Box();
private:
int i;
double d;
};
Box::Box(int _i, double _d):i(_i), d(_d) {}
Box::~Box() {}
bool operator<(const Box &left, const Box &right)
{
return (left.i < right.i);
}
ostream& operator<<(ostream &os, const Box &b)
{
os << b.d;
return os;
}
This the test code:
int main()
{
Box b1(3,2), b2(2,1), b3(0, 9);
map<Box, int> bmap;
bmap.insert(pair<Box,int>(b1, 10));
bmap.insert(pair<Box,int>(b2, 10));
bmap.insert(pair<Box,int>(b3, 10));
for (map<Box,int>::iterator iter = bmap.begin(); iter != bmap.end(); ++iter)
{
cout << iter->first << " ";
}
cout << endl;
return 0;
}
If I remove the definition of operator< on the Box class, the compiler will complain (an error) if I try to insert a Box object into std::map.
I have some experience with Java and I know in similar cases I just have to let Box implement Comarable. And Java compiler will check this contract at compile time, because Map in Java requires its key type conform to Comparable.
And if I want to define my own map type in Java, I just need to write:
public class MyMap<K extends Comparable<K>, V>
So my question is, if I want to implement my own map type (say, MyMap) in C++, how to define MyMap so that the compiler knows at compile time that "MyMap requires its key_type has its own overloaded definition of operator<"?
Long story short, you don't have to do anything: write your code as if the operator is there.
Unlike Java generics, C++ template mechanism can work without constraints, because the compiler is not required to produce any code until all class parameters are fully specified. In contrast, Java compilers must fully compile the class, and produce the final byte code without knowing the types that you plug in for K and V.
In other words, C++ compiler lets you call any functions and apply any operators you want in your template code. The template will compile without a problem if the classes that you supply have the corresponding functions and/or operators. If the functions and/or operators referenced from the template are missing, the compiler gives you an error message.
You do not need to specify any constraints in your generic type, like comparable in Java. By just using operator < in your templated class, makes this a requirement.
So in C++ you would just write:
template<typename K, typename V>
class MyMap {
..
if(a < b) {
..
}
What happens as soon as you instantiate a template, for example by writing MyMap<string, string> the compiler creates a new class by substituting K and V with string. If you put a type in without operator<, this will create a compile error.
Look at http://en.cppreference.com/w/cpp/container/map:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
The reason your compiler complaints about a missing '<'-operator is that the Compare-object std::less<Key> want's it. The keys are 'sorted by using the comparison function Compare', see C++ std::map key sort comparison function? for more information about how to implement your 'own' Compare-object. Usually you won't need to do this because the <-operator is implmented for fundamental types already (ints, floats etc) and for other types it is implemted as part of the STL:
https://sourceforge.net/p/stlport/code/ci/master/tree/stlport/stl/_string_operators.h#l347
template <class _CharT, class _Traits, class _Alloc>
inline bool _STLP_CALL
operator<(const basic_string<_CharT,_Traits,_Alloc>& __x,
const basic_string<_CharT,_Traits,_Alloc>& __y) {
return basic_string<_CharT,_Traits,_Alloc> ::_M_compare(__x.begin(), __x.end(),
__y.begin(), __y.end()) < 0;
}
Note: the Compare-object is not only used to sort the maps, but also determines if a key is considered 'existant in the map':
Internally, the elements in a map are always sorted by its
key following a specific strict weak ordering criterion indicated
by its internal comparison object (of type Compare).
And:
Compare:
A binary predicate that takes two element keys as arguments and returns
a bool. The expression comp(a,b), where comp is an object of this type
and a and b are key values, shall return true if a is considered to go
before b in the strict weak ordering the function defines.
The map object uses this expression to determine both the order the
elements follow in the container and whether two element keys are equivalent
(by comparing them reflexively: they are equivalent if !comp(a,b) && !comp(b,a)).
No two elements in a map container can have equivalent keys.
This can be a function pointer or a function object (see constructor for an
example). This defaults to `std::less<Key>`, which returns the same as applying the
less-than operator (a<b).
Aliased as member type map::key_compare.
(see http://www.cplusplus.com/reference/map/map/ ) Another good source of information is SGI's documentation of their STL-implementation: https://www.sgi.com/tech/stl/Map.html
Again, since in these docs are a lot of words and you would need to read them very very carefully:
they are equivalent if !comp(a,b) && !comp(b,a)
So, (since it felt onto my toes onces) you can construct a map<struct my*, int, my_cmp> where the my_cmp compare-function decides that 2 pointers of type my are NOT equal, allthough they are the same value:
struct my* a = &my_a;
struct my* b = a;
The output of my_cmp() decides, if a given key (and the associated value) are stored in the map or not. Very subtle.
Maybe interesting to read: https://latedev.wordpress.com/2013/08/12/less-than-obvious/ and http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/
Think of a template as an expression that can be used to generate code, not as code itself (that's actually how the templates got their names, prior to C++ templates some people would abuse the preprocessor to achieve the same goal). That is, when you write
template<class T> void foo(const T& bar) {
baz(bar);
}
it's pretty much the same as if you had written
#define foo(bar) baz(bar)
The contents of the definition (template or preprocessor) is pretty much irrelevant as long as it's not used. Only when the template is instanciated / the preprocessor directive is expanded, the compiler will check whether the result of the instanciation/expansion is valid.
As such, when a template uses a certain member function or operator on one of its arguments, it is the job of the user to supply a type that can be used in such a way, otherwise the compiler will do the substitution, look at the resulting code, shake its head, and throw an error message.

const objects as a function parameter

Consider the following code snippet:
class MyClass {
int x;
public:
MyClass(int val) : x(val) {}
const int& get() const {return x;}
};
void print (const MyClass& arg) {
cout << arg.get() << '\n';
}
int main() {
MyClass foo (10);
print(foo);
return 0;
}
Whether I add a const modifier before the instatiatation of MyClass or not, the program successfully compiles (without any warning) and prints 10. Why can print accept a non-const argument? Or, in other words, what is the function of the const modifier in the function parameter? Why can the formal and actual parameters of a function have different types (or modifiers)?
I have tried both GCC (4.8.2) and Clang (3.4) with -Wall -std=c++11 on Ubuntu 14.04, and the results were the same (no errors/warnings). I have also searched "c++ const object function" but didn't get anything that looked promising.
This is completely sane and normal. The object is treated as const within your function; it does not matter that it was not originally created to be immutable.
Of course, the opposite is not true!
void foo(T& rarr);
int main()
{
const T lolwut;
foo(lolwut); // oops
}
const forbids the body of the function from modifying the parameter variable. Both ways compile because you didn't attempt to modify it.
You can overload const and non-const reference parameters, and the const overload will only be chosen if the argument is really const (or a type conversion results in a temporary being passed). (For non-reference parameters, const seldom makes sense and such overloads may not even be defined.)
All that const does in this case is to prevent modification of parameter variable (and in the case of classes, prevent the calling of functions that are not labelled as const). MyClass may be trivially cast to const MyClass because there should be nothing that you can do to a const MyClass that you can't do to a non-const one. The reverse is not true, of course.
(I say "should" above, because it is of course possible to completely subvert const semantics under C++ if you wanted to, so the presence of const in a function prototype is really only a hopeful hint rather than a cast-iron compiler-enforced guarantee. But no sensible programmer should be breaking things like that!)

Can't push a map inside a vector?

The following code doesn't work, it gives the following errors:
No matching function for call to object of type 'const comparer'
and
Call to object of type 'value_compare' (aka 'std::_1::_map_value_compare, int, comparer, true>') is ambiguous
Here is the code:
struct comparer
{
bool operator()(const std::string x, const std::string y)
{
return x.compare(y)<0;
}
};
int main(int argc, char **argv)
{
vector< map<string,int,comparer> > valMapVect;
map<string,int,comparer> valMap;
valMapVect.push_back(valMap);
}
It is compiled with Xcode 5.x (so on a Mac).
Somebody has an idea of what is wrong? I think it was working a while ago when I was compiling it on Linux. Is it possible?
It seems libc++ wants the function call operator in comparer to be a const member function:
struct comparer
{
bool operator()(const std::string x, const std::string y) const
{ // ^^^^^ fixes the problem
return x.compare(y)<0;
}
};
Personally I would pass the arguments as std::string const& (note the &) but that doesn't change whether libc++ likes the comparison object. I'm not, yet, sure if the standard mandates that the const is present. I didn't spot such a requirement which would imply that the comparison function would have to be kept as a mutable member. However, given that it is often stateless, it is desirable to derive from it to not waste any memory (i.e., take advantage of the empty base optimization) which is probably what libc++ does. It isn't quite clear whether
it is a bug in libc++, i.e., that the comparison function object has to be stored a mutable member.
in the standard to not mandate the function call operator to be const.
in the code using it to make the function call operator const.
The easiest fix is, however, to make function call operator const.

Template neglects const (why?)

Does somebody know, why this compiles??
template< typename TBufferTypeFront, typename TBufferTypeBack = TBufferTypeFront>
class FrontBackBuffer{
public:
FrontBackBuffer(
const TBufferTypeFront front,
const TBufferTypeBack back): ////const reference assigned to reference???
m_Front(front),
m_Back(back)
{
};
~FrontBackBuffer()
{};
TBufferTypeFront m_Front; ///< The front buffer
TBufferTypeBack m_Back; ///< The back buffer
};
int main(){
int b;
int a;
FrontBackBuffer<int&,int&> buffer(a,b); //
buffer.m_Back = 33;
buffer.m_Front = 55;
}
I compile with GCC 4.4. Why does it even let me compile this? Shouldn't there be an error that I cannot assign a const reference to a non-const reference?
The thing is that if type T is int&, then the type const T is not const int&, but int & const. The illegal top-level const on a reference is ignored in template substitutions and typedef results.
If, on the other hand T is const int, then T& is const int&
When TypeBufferFront is int&, const TBufferTypeFront is equivalent to int& const, where the const is ignored during template substitution, since all references are constant, even if what they refer to is not.
So, when instantiated with int&, your constructor is effectively FrontBackBuffer(int&, int&), which works as given.
This is an example of why many people will use T const instead of const T, to make it clearer how the substitution occurs, as well as allow them to read the cv-qualifiers from right to left.
For the code to do what you want it to do, it would have to read:
FrontBackBuffer(
typename std::remove_reference<TBufferTypeFront>::type const& m_front,
typename std::remove_reference<TBufferTypeBack>::type const& m_back): ////const reference assigned to reference???
m_Front(m_front),
m_Back(m_back)
{
};
which has the added "feature" that it turns other types into const references when used to construct FrontBackBuffer.
Now this isn't perfect. This prevents temporary arguments to FrontBackBuffer from being moved, and passes even small cheap to copy types (like char) by reference instead of by value. There are standard C++0x techniques to do this that are a bit awkward to write if you care.
FrontBackBuffer::m_Front is of type TBufferTypeFront which translates to int& in your template instantiation. There is nothing wrong with assigning to an int&.

C++0x decltype fails to deduce member variable constness

Consider the following code:
template <typename T>
class B
{
};
template <typename T>
B<T> f(T& t)
{
return B<T>();
}
class A
{
class C {};
C c;
public:
A() {}
decltype(f(c)) get_c() const { return f(c); }
};
int main()
{
A a;
a.get_c();
}
When I try to compile this, I get the error:
test.cpp: In member function 'B<A::C> A::get_c() const':
test.cpp:31:46: error: conversion from 'B<const A::C>' to non-scalar type 'B<A::C>' requested
It seems that in the decltype, the compiler doesn't know that this is a const member function and therefore c is of type const C, and as a result incorrectly deduces the type of f(c) to be B<C> rather than B<const C> which is what it really is.
Am I doing something incorrectly, or is this a compiler bug? I use gcc 4.6, but 4.4 and 4.5 exhibit the same behaviour.
The compiler operates correctly according to the current C++0x WP. See this issue report, which is currently being worked on.
Possibly the final C++0x Standard won't change the meaning of your decltype application in the return type before the function name. You would need to move it to after the parameter list using -> decltype(f(c)), which hopefully will do The Right thing in final C++0x.
No, decltype is not supposed to take into account whether the function is const or not, because it can't. The same could have been written differently:
typedef decltype(f(c)) return_type;
return_type get_c() const { return f(c); }
Correction: decltype(f(c)) shouldn't even compile, because c is not static.
f needs to take an rvalue reference, not an lvalue reference.
I don't think you're allowed to use decltype on anything you wouldn't normally be able to call. I haven't been able to find anything in the standard that would allow you to access c, even within a decltype expression, outside of anywhere you could use c. Since you don't have a this pointer at the point you're trying to do your thing, I don't think you can do what you're trying to do. Doing so doesn't work in MSVC 2010 at least, and it has nothing to do with const.
I considered using declval to get one but you can't access A&&.c because A is an incomplete type at that point. I can't see anyway to do what you're trying to do other than something like so:
decltype(f(declval<C const>())) get_c() const { ... }