Extend unordered_map, override operator[], MSVC 2015 error - c++

I'll let you try to talk me out of this inheritance in a minute*, but for now I'm hoping to find out why this error happens.
Here's a stripped down example that reproduces the issue in MSVC 2015:
template<typename key_type, typename value_type>
class my_unordered_map : public unordered_map<key_type, value_type>
{
public:
value_type& operator[](const key_type& key)
{
return unordered_map<key_type, value_type>::operator[](key);
}
};
int main()
{
my_unordered_map<string, int> m;
m["x"] = 42;
}
All that my_unordered_map is intending to do (so far) is override one member function and delegate the call to its overridden base member function. But I get the error is MSVC 2015:
binary '=': no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
Any thoughts why?
* Okay, so now you can try to talk me out of this inheritance. The gist is I want an unordered_map + one extra feature. But aside from that feature, I still want to retain the entire interface and implementation of unordered_map, and inheritance seems the most straightforward way to do that. And even though the language will allow it, I don't plan to switch between the two kinds of maps polymorphically, so virtual or lack of shouldn't be an issue.

In MSVC's implementation (probably defined by the standard?) of map and unordered_map, the alias value_type is std::pair<const key_type, mapped_type>. The key_type and value_type of std::unordered_map are somehow hiding the template type parameters in your derived class. If you rename them as, say, key_type_ and value_type_ it should work.
I'm not sure why the typedefs in unordered_map would overshadow the template parameters for the class. I think this question asks about this, and the comments seem to indicate it is a MSVC extension.

Related

Using Boost strong typedef with unordered_map

I am using a Boost strong typedef to strong-type uint64_t:
BOOST_STRONG_TYPEDEF(uint64_t, MyInt)
and an std::unordered_map using this:
std::unordered_map<MyInt, int> umap;
Unfortunately I get a lot of compiler errors (can't paste here as they're on another machine):
error: static assertion failed: hash function must be invocable with an argument of key type
.static_assert(__is_invocable<const _H1&, const _Key&>)
error: use of deleted function 'std::__detail::_Hashtable_ebo_helper
_Hashtable() = default;
(+ a lot more I cannot type)
Using a Boost strong type as the key definitely causes the problem because if I remove the unordered_map I get no compiler errors.
From the boost documentation about BOOST_STRONG_TYPEDEF, it is clearly mentioned that it defines a new type wrapping the target inner type (it is not a simple alias).
Knowing that, there is no defined std::hash<MyInt> specialization available (required by std::unordered_map<MyInt, int>) since MyInt is now a distinct type from uint64_t.
You just need to provide one and it should work as expected.
For instance:
namespace std
{
template <>
struct hash<MyInt>
{
size_t operator()(const MyInt & m) const
{
return hash<uint64_t>{}(m);
}
};
}
Edit:
As pointed out in comments, you could also directly pass the proper std::hash<> specialization to use from the std::unordered_map<> instantiation as the third template parameter since MyInt is implicitly convertible to uint64_t (as mentioned in the boost documentation as well).
It would then become:
std::unordered_map<MyInt, int, std::hash<uint64_t>> umap;
No need to define your own std::hash<MyInt> specialization anymore.

Why is std::iterator deprecated?

Template class std::iterator is set to be deprecated in C++17. Why so? It has been a handy way to make sure std::iterator_traits works, especially if you can make use of the default template arguments. Is there some other way of doing it in C++17?
From the proposal that suggested its deprecation:
As an aid to writing iterator classes, the original standard library supplied the iterator class template to automate the declaration of the five typedefs expected of every iterator by iterator_traits. This was then used in the library itself, for instance in the specification of std::ostream_iterator:
template <class T, class charT = char, class traits = char_traits<charT> >
class ostream_iterator:
public iterator<output_iterator_tag, void, void, void, void>;
The long sequence of void arguments is much less clear to the reader than simply providing the expected typedefs in the class definition itself, which is the approach taken by the current working draft, following the pattern set in C++14 where we deprecated the derivation throughout the library of functors from unary_function and binary_function.
In addition to the reduced clarity, the iterator template also lays a trap for the unwary, as in typical usage it will be a dependent base class, which means it will not be looking into during name lookup from within the class or its member functions. This leads to surprised users trying to understand why the following simple usage does not work:
#include <iterator>
template <typename T>
struct MyIterator : std::iterator<std::random_access_iterator_tag, T> {
value_type data; // Error: value_type is not found by name lookup
// ... implementations details elided ...
};
The reason of clarity alone was sufficient to persuade the LWG to update the standard library specification to no longer mandate the standard iterator adapators as deriving from std::iterator, so there is no further use of this template within the standard itself. Therefore, it looks like a strong candidate for deprecation.
You can also see STL's reasoning in LWG 2438. (h/t T.C.)
As for some other way of doing it, not really. You could basically implement your own version of std::iterator (which isn't too hard) or manually write out all of those typedefs (which isn't too hard either, and I actually prefer it for clarity).
As Barry states, the working group has decided that explicitly declaring the types in the class is more readable and leads to less surprises than inheriting from std::iterator.
It's not too hard, though, to convert to the explicit types (below example taken from www.fluentcpp.com here). Given a class that was declared like so:
class MyIterator
: public std::iterator<std::forward_iterator_tag, int, int, int*, int&>
{
...
The class without std::iterator becomes:
class MyIterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
// ...

Function and method pointers in a std::set

As described in another message of mine, it is not possible to compare 2 pointers to member functions with "<" (less than). Or at least, this causes undefined behavior.
I have just managed to compile this code both with Visual C++ as well as GCC:
template <class Receiver, class Param = void*, class Return = void>
class EventReceiver : public IFunction<> {
protected:
std::set< Return(Receiver::*)(Param) > test;
std::set< Return(*)(Param) > test2;
...
AFAIK, to make a std::map or std::set of anything, it must be possible to compare the set's values with "<". Does this mean that the above containers or the actual compilers have a working implementation of comparing pointers-to-methods in such a way?
Well, it was in reality misleading that the example code compiled. The truth is that the sets are unusable. Every attempt to insert data into them produces the expected error.
That's the "dark side" of C++ template functions. They don't exist till you use them (and thus won't produce compiler errors till you do).
Check this out :
#include <set>
class X {};
int main() {
typedef void(X::*FuncPtr)();
std::set< FuncPtr > set;
std::less< FuncPtr > less;
FuncPtr f1;
FuncPtr f2;
//set.insert(f1); // both of these lines
//less(f1,f2); // produce an error
};
Removing the comments in any of the last 2 lines produces the error :
invalid operands of types ‘void (X::* const)()’ and ‘void (X::*
const)()’ to binary ‘operator<’
Compile it online yourself here.
std::set<T> uses std::less<T>, instead of using the less-than operator directly. When T is a pointer type, std::less<T> is guaranteed to be a total order, even if the less-than operator does not.
Edit: Section 20.3.3 of the C++98 standard says "pointer types", which (I think) includes pointers to functions, but not pointers to members.

Implementing a map_keys_iterator by derivation: a single compiler error

I was working again with C++ during the weekend and came to notice something that I'm not sure where does it come from.
Following the advice in this thread, I decided to implement a map_keys_iterator and map_values_iterator. I took the -- I think -- recommended-against approach of deriving a class from std::map<K,V>::iterator and implementing it as such:
template <typename K, typename V>
struct map_values_iterator:
public std::map<K,V>::iterator {
// explicitly call base's constructor
typedef typename std::map<K,V>::iterator mIterator;
map_values_iterator (const mIterator& mi) :
mIterator(mi) {};
const V& operator* () const { return (*this)->second; }
};
So far, so good, and the following code works (nvm the Unicode, I default to work with i18n-capable terminals):
typedef std::map<double,string> Map;
Map constants;
constants[M_PI] = "π";
constants[(1+sqrt(5))/2] = "φ";
constants[exp(M_PI)-M_PI] = "fake_20";
// ... fill map with more constants!
map_values_iterator<double, std::string> vs(constants.begin());
for (; vs != m.end(); ++vs) {
cout<< (vs != m.begin() ? ", " : "")<< *vs;
}
cout<< endl;
This code prints the expected result, something like (because a Map's elements are ordered):
..., φ, ..., π, ...., fake_20, ....
So I'd guess a map_keys_iterator would work in a similar way as well. I took the care that a Map's value_type is actually pair<const K, V> so the keys version will return a value.
However, it is unwieldly to have to declare the iterator's type so I wanted to create a caller with the classical make_pair-like idiom. And this is where trouble begins:
template <typename K, typename V>
map_values_iterator<K,V> map_values(const typename std::map<K,V>::iterator &i) {
return lpp::map_values_iterator<K,V> (i);
}
template <typename K, typename V>
map_values_iterator<K,V> map_values(const typename std::map<K,V>::const_iterator &i) {
return lpp::map_values_iterator<K,V> (i);
}
I'm relatively sure this function has the right signature and constructor invocation. However if I attempt to call the function from code:
auto vs= map_values(constants.begin());
I get a single STL compiler error of the form:
error: no matching function for call to ‘map_values(std::_Rb_tree_iterator<std::pair<const double, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)’
I'm assuming here that in this particular case the whole _Rb_tree_iterator is actually the correct iterator for which map::iterator is typedefed; I'm not completely sure however. I've tried to provide more overloads to see if one of them matches (drop the reference, drop the const, use only non-const_iterator variants, etc) but so far nothing allows the signature I'm interested in.
If I store the base iterator in a variable before calling the function (as in auto begin= constans.begin(); auto vs= map_values(begin);) I get the exact same base error, only the description of the unmatched call is obviously different (in that it is a "const blah&").
My first attempt at implementing this sort of iterator was by creating a base class that aggregated map::iterator instead of inheriting, and deriving two classes, each with the adequate operator*, but that version ran into many more problems than the above and still forced me to replicate too much of the interface. So I tried this option for code-expedition.
I've tried to look for answers to this issue but my Google-fu isn't very strong today. Maybe I am missing something obvious, maybe I forgot something with the derivation (although I'm almost sure I didn't -- iterators are unlike containers), maybe I am actually required to specify all the template parameters for the map, or maybe my compiler is broken, but whatever it is I can't find it and I am having real trouble understanding what is the actual thing the compiler is complaining about here. In my previous experience, if you are doing something wrong with the STL you are supposed to see a diarrhoea of errors, not only one (which isn't STL to boot).
So... any (well-encapsulated) pointers would be appreciated.
The reason is that your K and V type parameters are in a non-deducible context, so your function template is never even instantiated during overload resolution.
Look at it again:
template <typename K, typename V>
map_keys_iterator<K,V> map_keys(const typename std::map<K,V>::iterator &i)
For this to work, the C++ compiler would somehow have to walk from a specific iterator class to its "parent container" type - map in this case - to get its K and V. In general, this is impossible - after all, a particular iterator might be a typedef for some other class, and the actual type of argument in the call is that other class; there's no way the compiler can "retrace" it. So, per C++ standard, it doesn't even try in this case - and, more generally, in any case where you have typename SomeType<T>::OtherType, and T is a type parameter.
What you can do is make the entire parameter type a template type parameter. This requires some trickery to derive K and V, though.
template <typename Iterator>
map_keys_iterator<
typename std::iterator_traits<Iterator>::value_type::first_type,
typename std::iterator_traits<Iterator>::value_type::second_type
> map_keys(Iterator i)
Unfortunately, you'll have to repeat those two in the body of the function as well, when invoking the constructor of your type.
As a side note, iterators are generally passed by value (they're meant to be lightweight).

Is it possible to get the value type from an arbitrary iterator (C++)?

I have a class
template <typename Iterator, typename Value>
class Foo {
public:
Foo(const Iterator& it) { ... }
...
private:
map<Value, int> m_;
}
};
Is there any way to get rid of Value in the template? The Iterator may or may not be an STL iterator, but it's guaranteed that *it type is Value.
I know about iterator_traits<T>::value_type for STL iterators, but wonder if there's any way to get Value type automatically for an arbitrary Iterator type?
One trick I'm thinking about - say, we have a helper class
template <typename Iterator, typename Value>
class Bar {
public:
Bar(const Iterator& dummy_iterator, const Value& dummmy_value) {}
...
};
Then if we instantiate Bar as Bar(it, *it), the type of Value will be known inside Bar. But I can't find a good way to combine Bar with Foo.
Any iterator should provide iterator_traits<Iterator>::value_type. If it does not, then it is not an iterator. ISO C++ 2003 24.3.1[lib.iterator.traits] "Iterator traits":
To implement algorithms only in terms
of iterators, it is often necessary to
determine the value and difference
types that correspond to a particular
iterator type. Accordingly, it is
required that if Iterator is the type
of an iterator, the types
iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::iterator_category
be defined as the iterator’s
difference type, value type and
iterator category, respectively.
Aside from that, there's no general way to obtain a type of an arbitrary C++ expression. C++0x will rectify it by providing decltype.
Sorry. The correct way to get rid of Value is to use iterator_traits as you suggested.
If your non-STL iterator is a naked pointer, then you get correct iterator_traits typedefs for free. Otherwise the non-STL iterator class must define the correct typedefs.
See the iterator traits documentation for more information.
As to getting value type of iterator previous answers were correct.
But there is more. The trick you are thinking of would not work with class. If Bar was a function like:
template <typename Iterator, typename Value>
void bar(const Iterator& dummy_iterator, const Value& dummmy_value) {}
then type deduction would work for bar(it, *it) and you would have the value type inside of bar. (But keep in mind that to use this trick you would still have to have a dereferencable iterator it which is not always good - how to deal with empty sequence then?)
Using a class Bar you would have to provide the template arguments Iterator and Value manually as there is no type deduction for classes and using Bar(it, *it) would no compile.