As title says, is it allowed? If not, are they sharing the same interface or abstract class anyway? I did not find any reference from online documents. but looks unordered_map and map are using the same functions.
No, they're totally different and even if you force it with reinterpret_cast, it'll just go horribly wrong at runtime (ie, the dreaded Undefined Behaviour).
They're both STL containers, so deliberately have consistent interfaces.
That doesn't mean they're the same thing internally.
They're two unrelated types. If you want to construct one based on the other, you need to use constructors that take iterator range (C++11):
template <class InputIterator>
map(InputIterator first, InputIterator last,
const Compare& comp = Compare(), const Allocator& = Allocator());
template <class InputIterator>
unordered_map(InputIterator f, InputIterator l,
size_type n = see below,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
STL classes do not use virtual functions; there is no consistent base class you can cast through (ie, there's no equivalent to Java's java.util.Map). You can't cast between std::unordered_map and std::map any more than you could cast between HashMap and TreeMap in java - they're completely different types and cannot be used equivalently.
If you want a function to be able to take multiple types of STL containers, just use a template function:
template<typename Map>
void somefunc(Map &mymap) {
// ...
}
You can reinterpret cast one thing to any other thing, even if it makes no sense. The compiler won't stop you if you force it, which is essentially what reinterpret cast is. Casting map to unordered_map makes no sense & won't work.
Why do you want to cast map to unordered_map? They have the same interface. You gain nothing by casting it.
Related
I have functions that take in std::vector iterators, as in
typedef std::vector<Point> Points;
Points ConvexHull(Points::const_iterator first, Points::const_iterator last);
I usually pass the std iterators to them, but occasionally I need to work with boost iterators, such as boost::join's range iterator. How should I change the parametrizations of my functions, ideally without templates, so that they accept both iterators? Moreover, how do I indicate in each type which iterator concepts I need?
I tried looking at the boost::range documentation but it's overwhelmingly confusing for me and I don't know where to start.
For example, I couldn't find the difference between boost::range_details::any_forward_iterator_interface and boost::range_details::any_forward_iterator_wrapper, and whether I should use either of those to specify that I need a forward iterator.
Edit:
If I use boost::any_range, how can I pass non-const lvalue references?
For example:
template<typename T>
using Range = boost::any_range<T, boost::random_access_traversal_tag,
T, std::ptrdiff_t>;
f(Range<Point> &points); // defined elsewhere
// -------------
vector<Point> vec;
f(vec); // error; cannot bind non-const lvalue reference to unrelated type
boost-range has the any_range for this purpose and it suits both purposes for your case.
https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference/ranges/any_range.html
From your example it would look like this:
#include <boost/range/any_range.hpp>
typedef boost::any_range<Point,
boost::bidirectional_traversal_tag,
Point,
std::ptrdiff_t
> PointRange;
You should strongly consider using a template. Doing so let's the compiler keep useful information about what operations are actually occurring, which greatly helps it generate optimised output. The std:: convention is to name the type parameter for the concept required. E.g.
template< class BidirIt, class UnaryPredicate > // anything bidirectional (which includes random access)
BidirIt std::partition( BidirIt first, BidirIt last, UnaryPredicate p );
If you really don't want a template, you still shouldn't name anything in a detail namespace. Something like
#include <boost/range/any_range.hpp>
using PointRange = boost::any_range<Point, boost::random_access_traversal_tag>; // or another traversal tag.
using PointIterator = PointRange::iterator;
You will likely need to pass PointRange & less frequently than, say, int *&. Almost always passing by value is the correct behaviour. It is cheap to copy, as it holds a begin and end iterator from the Range that it was constructed from, nothing more.
C++ STL uses a red-black tree to store data inside std::set and std::map. I noticed that set::iterator is actually a typedef of the const iterator of the red black tree:
//All code snippets taken from SGI STL. https://www.sgi.com/tech/stl/
typedef _Rb_tree<key_type, value_type, _Identity<value_type>, key_compare, _Alloc> _Rep_type;
typedef typename _Rep_type::const_iterator iterator;
This is reasonable because users are supposed not to modify the content of the set through an iterator. But set has to implement operations like insert and erase, which calls for a non-const iterator of the red-black tree. SGI STL uses a c-style cast to do this:
void erase(iterator __position) {
typedef typename _Rep_type::iterator _Rep_iterator;
_M_t.erase((_Rep_iterator&)__position);
}
I'm wondering:
Why is this cast safe? It is casting _Rep_type::const_iterator to _Rep_type::iterator&.
How to write the cast in C++ style? I've tried to do it: Neither static_cast nor const_cast will do the job. reinterpret_cast can compile, but I'm not sure if it does the same thing as the C-style cast.
iterator and _Rep_type::iterator are instances of the same class template, the former using const qualified types and the latter using the same, but non-const types. Something like this:
template <class T, class U>
struct B {};
using S = B<int&, int*>;
using Sconst = B<const int&, const int*>;
So for your questions:
It is safe because the two types have the exact same memory layout.
You cannot use static_cast because the compiler considers the types to be unrelated. You have to bring in the heavy artillery, reinterpret_cast:
int test() {
S s;
Sconst& sconst = reinterpret_cast<Sconst&>(s);
}
A C-Cast has no direct mapping to one specific C++ cast.
But a C-Cast can be mapped to one or more C++ casts.
When doing a C-Cast you can think of the compiler trying this set of C++ casts in order. The first one that is valid is what the C++ compiler will do: (see also C++ Standard, In N4917 7.6.3 Explicit type conversion (cast notation) [expr.cast])
const_cast
static_cast
static_cast followed by const_cast
reinterpret_cast
reinterpret_castfollowed by const_cast
So looking at your example, it looks like option 3 or 5 (but would need to try it with a compiler to be sure).
(_Rep_iterator&)__position
// Maps to:
const_cast<_Rep_iterator&>(static_cast<_Rep_iterator const&>(__position))
// or
const_cast<_Rep_iterator&>(reinterpret_cast<_Rep_iterator const&>(__position))
Say I'm making a function to copy a value:
template<class ItInput, class ItOutput>
void copy(ItInput i, ItOutput o) { *o = *i; }
and I would like to avoid the assignment if i and o point to the same object, since then the assignment is pointless.
Obviously, I can't say if (i != o) { ... }, both because i and o might be of different types and because they might point into different containers (and would thus be incomparable). Less obviously, I can't use overloaded function templates either, because the iterators might belong to different containers even though they have the same type.
My initial solution to this was:
template<class ItInput, class ItOutput>
void copy(ItInput i, ItOutput o)
{
if (&*o != static_cast<void const *>(&*i))
*o = *i;
}
but I'm not sure if this works. What if *o or *i actually returns an object instead of a reference?
Is there a way to do this generally?
I don't think that this is really necessary: if assignment is expensive, the type should define an assignment operator that performs the (relatively cheap) self assignment check to prevent doing unnecessary work. But, it's an interesting question, with many pitfalls, so I'll take a stab at answering it.
If we are to assemble a general solution that works for input and output iterators, there are several pitfalls that we must watch out for:
An input iterator is a single-pass iterator: you can only perform indirection via the iterator once per element, so, we can't perform indirection via the iterator once to get the address of the pointed-to value and a second time to perform the copy.
An input iterator may be a proxy iterator. A proxy iterator is an iterator whose operator* returns an object, not a reference. With a proxy iterator, the expression &*it is ill-formed, because *it is an rvalue (it's possible to overload the unary-&, but doing so is usually considered evil and horrible, and most types do not do this).
An output iterator can only be used for output; you cannot perform indirection via it and use the result as an rvalue. You can write to the "pointed to element" but you can't read from it.
So, if we're going to make your "optimization," we'll need to make it only for the case where both iterators are forward iterators (this includes bidirectional iterators and random access iterators: they're forward iterators too).
Because we're nice, we also need to be mindful of the fact that, despite the fact that it violates the concept requirements, many proxy iterators misrepresent their category because it is very useful to have a proxy iterator that supports random access over a sequence of proxied objects. (I'm not even sure how one could implement an efficient iterator for std::vector<bool> without doing this.)
We'll use the following Standard Library headers:
#include <iterator>
#include <type_traits>
#include <utility>
We define a metafunction, is_forward_iterator, that tests whether a type is a "real" forward iterator (i.e., is not a proxy iterator):
template <typename T>
struct is_forward_iterator :
std::integral_constant<
bool,
std::is_base_of<
std::forward_iterator_tag,
typename std::iterator_traits<T>::iterator_category
>::value &&
std::is_lvalue_reference<
decltype(*std::declval<T>())
>::value>
{ };
For brevity, we also define a metafunction, can_compare, that tests whether two types are both forward iterators:
template <typename T, typename U>
struct can_compare :
std::integral_constant<
bool,
is_forward_iterator<T>::value &&
is_forward_iterator<U>::value
>
{ };
Then, we'll write two overloads of the copy function and use SFINAE to select the right overload based on the iterator types: if both iterators are forward iterators, we'll include the check, otherwise we'll exclude the check and always perform the assignment:
template <typename InputIt, typename OutputIt>
auto copy(InputIt const in, OutputIt const out)
-> typename std::enable_if<can_compare<InputIt, OutputIt>::value>::type
{
if (static_cast<void const volatile*>(std::addressof(*in)) !=
static_cast<void const volatile*>(std::addressof(*out)))
*out = *in;
}
template <typename InputIt, typename OutputIt>
auto copy(InputIt const in, OutputIt const out)
-> typename std::enable_if<!can_compare<InputIt, OutputIt>::value>::type
{
*out = *in;
}
As easy as pie!
I think this may be a case where you may have to document some assumptions about the types you expect in the function and be content with not being completely generic.
Like operator*, operator& could be overloaded to do all sorts of things. If you're guarding against operator*, then you should consider operator& and operator!=, etc.
I would say that a good prerequisite to enforce (either through comments in the code or a concept/static_assert) is that operator* returns a reference to the object pointed to by the iterator and that it doesn't (or shouldn't) perform a copy. In that case, your code as it stands seems fine.
Your code, as is, is definitly not okay, or atleast not okay for all iterator categories.
Input iterators and output iterators are not required to be dereferenceable after the first time (they're expected to be single-pass) and input iterators are allowed to dereference to anything "convertible to T" (§24.2.3/2).
So, if you want to handle all kinds of iterators, I don't think you can enforce this "optimization", i.e. you can't generically check if two iterators point to the same object. If you're willing to forego input and output iterators, what you have should be fine. Otherwise, I'd stick with doing the copy in any case (I really don't think you have another option on this).
Write a helper template function equals that automatically returns false if the iterators are different types. Either that or do a specialization or overload of your copy function itself.
If they're the same type then you can use your trick of comparing the pointers of the objects they resolve to, no casting required:
if (&*i != &*o)
*o = *i;
If *i or *o doesn't return a reference, no problem - the copy will occur even if it didn't have to, but no harm will be done.
I like to know how things work and as such have been delving deep into the c++ standard library. Something occurred to me the other day.
It is required that containters (for example: std::vector<int, std::allocator<int> >) use the allocator specified for allocations. Specifically the standard says:
23.1.8
Copy constructors for all container types defined in this clause copy
an allocator argument from their respective first parameters. All
other constructors for these container types take an Allocator&
argument (20.1.5), an allocator whose value type is the same as the
container’s value type. A copy of this argument is used for any memory
allocation performed, by these constructors and by all member
functions, during the lifetime of each container object. In all
container types defined in this clause, the member get_allocator()
returns a copy of the Allocator object used to construct the
container.
Also later in the standard it says (in a few different spots but i'll pick one) things like this:
explicit deque(size_type n, const T& value = T(), const Allocator& = Allocator());
Effects: Constructs a deque with n copies of value,
using the specified allocator.
OK, so on to my question.
Let's take std::vector as an example, the natural and efficient way to implement something like:
vector<T, A>::vector(const vector& x)
might look something like this:
template <class T, class A>
vector<T, A>::vector(const vector& x) {
pointer p = alloc_.allocate(x.size());
std::uninitialized_copy(x.begin(), x.end(), p);
first_ = p;
last_ = p + x.size();
end_ = p + x.size();
}
specifically, we allocate some memory and then copy construct all the members in place. Not bothering to do something like new value_type[x.size()] because that would default construct the array only to overwrite it!.
but, this doesn't use the allocator to do the copy construction...
I could manually write a loop which does something like this:
while(first != last) {
alloc_.construct(&*dest++, *first++);
}
but that's a waste, it's nearly identical to std::uninitialized_copy, the only difference is that is uses the allocator instead of placement new.
So, would you consider it an oversight that the standard doesn't have the (seemingly obvious to me) set of functions like these:
template <class In, class For, class A>
For uninitialized_copy(In first, In last, For dest, A &a);
template <class In, class Size, class For, class A>
For uninitialized_copy_n(In first, Size count, For dest, A &a);
template <class For, class T, class A>
void uninitialized_fill(For first, For last, const T& x, A &a);
template <class For, class Size, class T, class A>
void uninitialized_fill_n(For first, Size count, const T& x, A &a);
I would imagine that these types of functions (even though they are trivial to implement manually... until you try to make them exception safe) would prove fairly useful if people want to implement there own containers and such and make efficient use of copy construction while using allocators.
Thoughts?
I'm not sure whether we could call it an "oversight", per se.
No, you can't provide your own allocator to these specialised algorithms. But then there are other things that the standard doesn't contain, either.
#MarkB identifies a very good reason that the standard shouldn't do this (that the range has no knowledge of the container's allocator). I'd go so far as to say it's just an inherent limitation.
You can always re-invent uninitialized_copy for your needs, knowing what the allocator should be. It's just a two-line for loop.
If these functions were free-functions, I can't see any way that the compiler could detect allocator mismatches since the allocator type isn't retained by the iterator. This in turn could result in a variety of hard-to-find problems.
Yes, I think it is a (big) oversight, because information about the allocator is lost otherwise. The allocator is, in the current protocols, the only one that knows how to exactly construct an object in memory.
Now Boost includes alloc_construct, alloc_destroy https://www.boost.org/doc/libs/1_72_0/libs/core/doc/html/core/alloc_construct.html
which at least can help a bit implementing generic versions of uninitialized_copy/fill/etc(Alloc a, ...).
I've run into some annoying issues with const-correctness in some templated code, that ultimately boils down to the following observation: for some reason, given an STL-ish Container type T, const typename T::pointer does not actually seem to yeild a constant pointer type, even if T::pointer is equivalent to T::value_type*.
The following example illustrates the problem. Suppose you have a templated function that takes a Container which must meet the STL Random Access Container concept requirements.
template <class Container>
void example(Container& c)
{
const typename Container::pointer p1 = &c[0]; // Error if c is const
const typename Container::value_type* p2 = &c[0];
}
Then, if we pass this function a const container...
const std::vector<int> vec(10);
example(vec);
...we get an invalid conversion from const int* to int*. But why is const typename Container::pointer not the same as const int* in this example?
Note that if I change const typename Container::pointer to simply typename Container::const_pointer it compiles fine, however, as far as I can tell, the const_pointer typedef is an extension, (I don't see it mentioned in the C++ standard Container Requirements (23.5, Table 65)), and so therefore I don't want to use it.
So how can I obtain a generic, const-correct pointer type from a container T? (I really can't see how to do this without using boost::mpl::if_ along with type_traits to check if the container is constant...but there must be a less verbose way to do this)
Edit: In case it matters, I'm using gcc 4.3.2 to compile this.
It doesn't work because your const does not apply to what you think it applies to. For example, if you have
typedef int* IntPtr;
then
const IntPtr p;
does not stand for
const int* p;
but rather stands for
int* const p;
Typedef-name is not a macro. Once the "pointerness" of the type is wrapped into a typedef-name, there's no way to use it to create a pointer-to-const type anymore. I.e. there's absolutely no way to use the above IntPtr typedef-name to produce an equivalent of
const int* p;
You have to either use to pointee type explicitly (as you did with value_type), or check whether your container defines a different typedef-name, with const already wrapped "inside" (like const_pointer or something like that).
This:
typename Container::pointer
Has the type int* (in our case). I don't know the terminology, so sorry for that, but pointers point to a type. That is, Container::pointer is a pointer to a mutable T, and adding const is only going to make this a const pointer (not a pointer to const), because Container::pointer has already been defined to point to a mutable T.
It seems only const_pointer, either from the class or your own:
typedef const typename Container::value_type* const_pointer
Will work.
The allocator requirements (cf. 20.1.5, Table 32, "Allocator requirements") state that allocators for the STL containers shall have an Allocator::const_pointer. All of the STL containers (the eight containers listed in Section 23) define a Container::const_pointer typedef as:
typedef typename Allocator::const_pointer const_pointer;
As you state in your question, however, containers (other than those eight) are not required to have a Container::const_pointer typedef.
This might be of relevance here (from SGI STL reference):
[6] As in the case of references [5], the pointer type must have the same semantics as C++ pointers but need not actually be a C++ pointer. "Smart pointers," however, unlike "smart references", are possible. This is because it is possible for user-defined types to define the dereference operator and the pointer member access operator, operator* and operator->.
It is possible by using a template. The trick is to remove the pointer first, then apply const on the type and finally applying pointer to the type - for instance using std::remove_pointer yields:
typedef const std::remove_pointer<Container::pointer>::type* PointerToConst;