Function and method pointers in a std::set - c++

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.

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.

What C++20 change to reverse_iterator is breaking this code?

The following code compiles in C++11, C++14, and C++17, but does not compile in C++20. What change to the standard broke this code?
#include <vector>
#include <utility>
template<typename T>
struct bar
{
typename T::reverse_iterator x;
};
struct foo
{
bar<std::vector<std::pair<foo, foo>*>> x;
};
int main()
{
foo f;
}
The error is quite long, but can be summarized as:
template argument must be a complete class
This was always undefined. [res.on.functions]/2.5 says:
In particular, the effects are undefined in the following cases:
[...]
If an incomplete type ([basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
std::pair does not (and cannot) support incomplete types. You were just relying on order of instantiation to kind of get around that. Something changed in the library that slightly changed the evaluation order, leading to the error. But undefined behavior is undefined - it happened to work before and it happens to not work now.
As to why it's specifically C++20 that is causing this to fail. In C++20, iterators changed to have this new iterator_concept idea. In order to instantiate that, reverse_iterator needs to determine what the concept should be. That looks like this:
#if __cplusplus > 201703L && __cpp_lib_concepts
using iterator_concept
= conditional_t<random_access_iterator<_Iterator>,
random_access_iterator_tag,
bidirectional_iterator_tag>;
using iterator_category
= __detail::__clamp_iter_cat<typename __traits_type::iterator_category,
random_access_iterator_tag>;
#endif
Now, in the process of checking random_access_iterator, the root of iterator concept hierarchy is wonderfully named input_or_output_iterator, specified in [iterator.concept.iterator]:
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
So, we have to do *i on our iterator type. That's __gnu_cxx::__normal_iterator<std::pair<foo, foo>**, std::vector<std::pair<foo, foo>*> > , in this case. Now, *i triggers ADL - because of course it does. And ADL requires instantiation of all the associated types - because those associated types might have injected friends that could be candidates!
This, in turn, requires instantiating pair<foo, foo> - because, we have to check. And then that ultimately fails in this specific case because instantiating a type requires instantiating all of the type's special member functions, and the way that libstdc++ implements conditional assignment for std::pair is using Eric Fisellier's trick:
_GLIBCXX20_CONSTEXPR pair&
operator=(typename conditional<
__and_<is_copy_assignable<_T1>,
is_copy_assignable<_T2>>::value,
const pair&, const __nonesuch&>::type __p)
{
first = __p.first;
second = __p.second;
return *this;
}
And is_copy_assignable requires complete types and we don't have one.
But really even if pair used concepts to check in this case, that would still involve instantiating the same type traits, so we'd ultimately end up in the same position.
Moral of the story is, undefined behavior is undefined.

Does std::map<K,V>::iterator instantiate std::map<K,V>?

I have this code, which works on GCC:
#include <map>
class Foo;
class Bar;
typedef std::map<Foo,Bar> MyMap;
MyMap::iterator i;
class Foo
{
MyMap::iterator some_data;
};
The code as currently designed (which is unpleasantly circular yes I'm stuck with it) requires map<Foo,Bar>::iterator to be available to Foo and Bar.
It works because the GCC library implementation happens to not need to instantiate the map's key type in order to instantiate the iterator.
Is this guaranteed? The standard seems to be somewhat hands-off when it comes to defining the map iterator type. How portable is this code?
This results in undefined behavior.
In the declaration MyMap::iterator i;, MyMap is required to be a complete type, thus it is implicitly instantiated. However, Foo and Bar are not complete at this point of instantiation, so the behavior is undefined according to [res.on.functions]/2:
In particular, the effects are undefined in the following cases:
...
if an incomplete type ([basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
You can side-step the entire question by considering the fact that std::map is a node-based container, so that its nodes containing elements have stable addresses. I.e. you can use a plain pointer instead of an iterator (as long as you don't need an iterator to pass into member functions of the std::map, of course):
class Foo
{
std::pair<Foo const, Bar>* some_data;
};
Note that only declarations for Foo, Bar and std::pair<> are needed here to define member some_data.
If you use boost::multi_index (which is superior to std associative containers in many ways) then it has extremely useful functions to_iterator that takes a reference to the element and returns an iterator to it.

Extend unordered_map, override operator[], MSVC 2015 error

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.

C++: type_info to distinguish types

I know that compilers have much freedom in implementing std::type_info functions' behavior.
I'm thinking about using it to compare object types, so I'd like to be sure that:
std::type_info::name must return two different strings for two different types.
std::type_info::before must say that Type1 is before Type2 exclusive-or Type2 is before Type1.
// like this:
typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
Two different specialization of the same template class are considered different types.
Two different typedef-initions of the same type are the same type.
And finally:
Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?
How fast are operator==, operator!= and before on most common compilers? I guess they should only compare a value. And how fast is typeid?
I've got a class A with a virtual bool operator==( const A& ) const. Since A has got many subclasses (some of which are unknown at compile time), I'd overload that virtual operator in any subclass B this way:
virtual bool operator==( const A &other ) const {
if( typeid(*this) != typeid(other) ) return false;
// bool B::operator==( const B &other ) const // is defined for any class B
return operator==( static_cast<B&>( other ) );
}
Is this an acceptable (and standard) way to implement such operator?
After a quick look at the documentation, I would say that :
std::type_info::name always returns two different strings for two different types, otherwise it means that the compiler lost itself while resolving types and you shouldn't use it anymore.
Reference tells : "before returns true if the type precedes the type of rhs in the collation order. The collation order is just an internal order kept by a particular implementation and is not necessarily related to inheritance relations or declaring order."
You therefore have the guarantee that no types has the same rank in the collation order.
Each instantiation of a template class is a different type. Specialization make no exceptions.
I don't really understand what you mean. If you mean something like having typedef foo bar; in two separate compilation units and that bar is the same in both, it works that way. If you mean typedef foo bar; typedef int bar;, it doesn't work (except if foo is int).
About your other questions :
You should store references to std::type_info, of wrap it somehow.
Absolutely no idea about performance, I assume that comparison operators have constant time despite of the type complexity. Before must have linear complexity depending on the number of different types used in your code.
This is really strange imho. You should overload your operator== instead of make it virtual and override it.
Standard 18.5.1 (Class type_info) :
The class type_info describes type
information generated by the
implementation. Objects of this class
effectively store a pointer to a name
for the type, and an encoded value
suitable for comparing two types for
equality or collating order. The
names, encoding rule, and collating
sequence for types are all unspecified
and may differ between programs.
From my understanding :
You don't have this guarantee regarding std:type_info::name. The standard only states that name returns an implementation-defined NTBS, and I believe a conforming implementation could very well return the same string for every type.
I don't know, and the standard isn't clear on this point, so I wouldn't rely on such behavior.
That one should be a definite 'Yes' for me
That one should be a definite 'Yes' for me
Regarding the second set of questions :
No, you cannot store a type_info. Andrei Alexandrescu proposes a TypeInfo wrapper in its Modern C++ Design book. Note that the objects returned by typeid have static storage so you can safely store pointers without worrying about object lifetime
I believe you can assume that type_info comparison are extremely efficient (there really isn't much to compare).
You can store it like this.
class my_type_info
{
public:
my_type_info(const std::type_info& info) : info_(&info){}
std::type_info get() const { return *info_;}
private:
const std::type_info* info_;
};
EDIT:
C++ standard 5.2.8.
The result of a
typeid expression is an lvalue of
static type const std::type_info...
Which means you can use it like this.
my_type_info(typeid(my_type));
The typeid function returns an lvalue (it is not temporary) and therefore the address of the returned type_info is always valid.
The current answers for questions 1 and 2 are perfectly correct, and they're essentially just details for the type_info class - no point in repeating those answers.
For questions 3 and 4, it's important to understand what precisely is a type in C++, and how they relate to names. For starters, there are a whole bunch of predefined types, and those have names: int, float, double. Next, there are some constructed types that do not have names of their own: const int, int*, const int*, int* const. There are function types int (int) and function pointer types int (*)(int).
It's sometimes useful to give a name to an unnamed type, which is possible using typedef. For instance, typedef int* pint or typedef int (*pf)(int);. This introduces a name, not a new type.
Next are user-defined types: structs, classes, unions. There's a good convention to give them names, but it's not mandatory. Don't add such a name with typedef, you can do so directly: struct Foo { }; instead of typedef struct {} Foo;. It's common to have class definitions in headers, which end up\ in multiple translation units. That does mean the class is defined more than once. This is still the same type, and therefore you aren't allowed to play tricks with macros to change the class member definitions.
A template class is not a type, it's a recipe for types. Two instantiations of a single class template are distinct types if the template arguments are different types (or values). This works recursively: Given template <typename T> struct Foo{};, Foo<Foo<int> > is the same type as Foo<Foo<Bar> > if and only if Bar is another name for the type int.
Type_info is implementation defined so I really wouldn't rely on it. However, based on my experiences using g++ and MSVC, assumptions 1,3 and 4 hold... not really sure about #2.
Is there any reason you can't use another method like this?
template<typename T, typename U>
struct is_same { static bool const result = false; };
template<typename T>
struct is_same<T, T> { static bool const result = true; };
template<typename S, typename T>
bool IsSame(const S& s, const T& t) { return is_same<S,T>::result; }
Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?
This is why std::type_index exists -- it's a wrapper around a type_info & that is copyable and compares (and hashes) by using the underlying type_info operations