Which C++20 standard library containers have a .at member access function? - c++

Which C++20 standard library containers have a .at function? For example, at least std::map, std::unordered_map and std::vector do. What others are there?
Is there some way to work this out without going through the 2000 page standard by hand?

From comments, it seems you want something like:
template <typename Container, typename T>
typename Container::pointer my_at(Container&, const T&)
requires (requires(Container c, const T& key) { c.at(key); })
{
// ...
}
Demo

I don't think a pointer is the appropriate type to return for this operation, instead it should be an iterator. At which point the definition splits into two cases:
Random access containers:
iterator at_(size_type index) noexcept { return index < size() ? begin() + index : end(); }
const_iterator at_(size_type index) const noexcept { return index < size() ? begin() + index : end(); }
map and unordered_map:
template<typename T>
iterator at_(T && key) noexcept { return find(std::forward<T>(key)); }
template<typename T>
const_iterator at_(T && key) const noexcept { return find(std::forward<T>(key)); }
And then rather than testing against nullptr, you test against the container's end().
It's also a reasonable question as to whether (unordered_)map needs an alias for an existing member to match your nomenclature

I ended up grepping libstdc++ include directory for \bat( and that has given me:
std::basic_string
std::basic_string_view
std::array
std::vector
std::vector<bool>
std::unordered_map
std::map
std::dequeue
I'm not sure if this is exhaustive.
There is also rope and vstring but I don't think these are standard.

Related

How do I implement a begin() member function in my custom data structure?

I'm trying to create my own data-structure that can determine if a value is an element within it in O(1) time with a hash-map.
I'm working on adding more member functions so that it's more similar to the STL containers. Here's what I currently have that's relevant to the problem:
template <class T>
class setfind {
private:
long long _size = 0;
unordered_map<T, bool> _set;
public:
// constructors
setfind() {}
// initialize from some iterable
template <class InputIterator>
setfind(InputIterator beg, InputIterator en){
_size = en - beg;
while (beg != en){
_set[*beg] = true;
beg++;
}
}
bool contains(const T &val){
return _set[val];
}
bool contains(const T &&val){
return _set[val];
}
};
As you can see, its core is the unordered_map. I want to write a member function that returns a begin iterator to the _set variable. I tried putting this in:
template <class InputIterator>
InputIterator begin()
{
return _set.begin();
}
but that led to a compilation error saying that there was no matching member function.
I don't know enough about iterators and template-classes to fix this. Does anyone know how to implement it or what's wrong? Are there any good resources so that I can learn more about this?
Also some optimization tips for this class would be appreciated because this is going to be used under a time-limit.
EDIT: I’m restricted to using c++11
EDIT2: Fixed a bug in the constructor
EDIT3: Memory leaks and best-practices will not be an issue
I see several issues with your code.
You don't need the _size member at all, get rid of it and use _set.size() when needed.
In your constructor, while (*beg != *en) needs to be while (beg != en) instead. Or better, just geet rid of the manual loop altogether and use the std::unordered_map constructor that takes an iterator pair as input:
// initialize from some iterable
template <class InputIterator>
setfind(InputIterator beg, InputIterator en) : _set(beg, en) {}
In your contains() methods, use _set.find() or _set.contains() instead of _set.operator[] to avoid val begin added to the _set if it does not already exist. Also, it does not make sense to take a const rvalue reference, so just get rid of that overload altogether:
bool contains(const T &val) const
{
return _set.find(val) != _set.end();
// or:
// return _set.contains(val);
}
And lastly, for your begin() method, just use auto for the return type instead of a template, let the compiler deduce the necessary type, eg:
auto begin()
{
return _set.begin();
}
UPDATE: apparent auto return type deduction was introduced in C++14. So, for C++11, you will just have to state the type explicitly, still don't use a template for it, eg:
unordered_map<T, bool>::iterator begin()
{
return _set.begin();
}
Or:
auto begin() -> unordered_map<T, bool>::iterator
{
return _set.begin();
}
Or:
auto begin() -> decltype(_set.begin())
{
return _set.begin();
}
You can simplify this by declaring an iterator alias in your class (which you should do anyway, if your goal is to make your class act like a standard container), eg:
template <class T>
class setfind {
private:
unordered_map<T, bool> _set;
public:
using iterator = unordered_map<T, bool>::iterator;
...
iterator begin(){
return _set.begin();
}
};

Is there a flat unsorted map/set implementation?

There is the boost.container flat_map and others, and the Loki AssocVector and many others like these which keep the elements sorted.
Is there a modern (c++11 move-enabled, etc.) implementation of an unsorted vector adapted as a map/set?
The idea is to use it for very small maps/sets (less than 20 elements) and with simple keys (for which hashing wouldn't always make sense)
Something like this?
template<class Key, class Value, template<class...>class Storage=std::vector>
struct flat_map {
struct kv {
Key k;
Value v;
template<class K, class V>
kv( K&& kin, V&& vin ):k(std::forward<K>(kin)), v(std::forward<V>(vin)){}
};
using storage_t = Storage<kv>;
storage_t storage;
// TODO: adl upgrade
using iterator=decltype(std::begin(std::declval<storage_t&>()));
using const_iterator=decltype(std::begin(std::declval<const storage_t&>()));
// boilerplate:
iterator begin() {
using std::begin;
return begin(storage);
}
const_iterator begin() const {
using std::begin;
return begin(storage);
}
const_iterator cbegin() const {
using std::begin;
return begin(storage);
}
iterator end() {
using std::end;
return end(storage);
}
const_iterator end() const {
using std::end;
return end(storage);
}
const_iterator cend() const {
using std::end;
return end(storage);
}
size_t size() const {
return storage.size();
}
bool empty() const {
return storage.empty();
}
// these only have to be valid if called:
void reserve(size_t n) {
storage.reserve(n);
}
size_t capacity() const {
return storage.capacity();
}
// map-like interface:
// TODO: SFINAE check for type of key
template<class K>
Value& operator[](K&& k){
auto it = find(k);
if (it != end()) return it->v;
storage.emplace_back( std::forward<K>(k), Value{} );
return storage.back().v;
}
private: // C++14, but you can just inject the lambda at point of use in 11:
template<class K>
auto key_match( K& k ) {
return [&k](kv const& kv){
return kv.k == k;
};
}
public:
template<class K>
iterator find(K&& k) {
return std::find_if( begin(), end(), key_match(k) );
}
template<class K>
const_iterator find(K&& k) const {
return const_cast<flat_map*>(this)->find(k);
}
// iterator-less query functions:
template<class K>
Value* get(K&& k) {
auto it = find(std::forward<K>(k));
if (it==end()) return nullptr;
return std::addressof(it->v);
}
template<class K>
Value const* get(K&& k) const {
return const_cast<flat_map*>(this)->get(std::forward<K>(k));
}
// key-based erase: (SFINAE should be is_comparible, but that doesn't exist)
template<class K, class=std::enable_if_t<std::is_converible<K, Key>{}>>
bool erase(K&& k) {
auto it = std::remove(
storage.begin(), storage.end(), key_match(std::forward<K>(k))
);
if (it == storage.end()) return false;
storage.erase( it, storage.end() );
return true;
}
// classic erase, for iterating:
iterator erase(const_iterator it) {
return storage.erase(it);
}
template<class K2, class V2,
class=std::enable_if_t<
std::is_convertible< K2, Key >{}&&
std::is_convertible< V2, Value >{}
>
>
void set( K2&& kin, V2&& vin ) {
auto it = find(kin);
if (it != end()){
it->second = std::forward<V2>(vin);
return;
} else {
storage.emplace_back( std::forward<K2>(kin), std::forward<V2>(vin) );
}
}
};
I left the container type as a template argument, so you can use a SBO vector-like structure if you choose.
In theory, I should expose a template parameter for replacing equals on the keys. I did, however, make the key-search functions transparent.
If the sets are sure to be small then you can just use a std::vector (or std::deque) and do lookup using linear searches. An O(n) linear search over a small vector can be faster than an O(log(n)) search over a more complicated structure such as a red-black tree.
So you could just put elements in a vector without sorting them. You would still need to do some shuffling if you remove elements, but that will always be true for a flat vector-like structure whether it's sorted or not, unless you only ever remove the back element. Why is it important that it's flat anyway?
There's std::unordered_set and std::unordered_map but as far as I know they are not implemented using vectors.
A possible option is to write your own hash vector and hash the key using std::hash<Key> and then index the resulting number modulo the length of the vector, but then you'll have to figure out a way to handle collisions and all the resulting problems manually. Not sure I recommended that.
An alternative would be to pass a custom allocator to std::unordered_set and std::unordered_map which perform the allocation on a vector (for example by owning an internal vector), as suggested by #BeyelerStudios.
Evgeny Panasyuk is correct, I believe what you want is an Open Address Hash Map.
This fits exactly your requirement, only 1 flat buffer, no allocation of nodes, no pointers to follow, and unsorted.
Otherwise you also have flat_map/AssocVectorbut they are sorted, unlike your requirement.
For OAHM, I have an implementation of a STL-like generic one here:
https://sourceforge.net/projects/cgenericopenaddresshashmap/
Also you might want to take a look the benchmark page of flat_map:
boost::flat_map and its performance compared to map and unordered_map
The OAHM is performing very close to the flat_map in all tests, except iteration.
Please look at the sfl library that I have recently updated to GitHub: https://github.com/slavenf/sfl-library
It is C++11 header-only library that offers flat ordered and unordered containers that store elements contiguously in memory. All containers meet requirements of Container, AllocatorAwareContainer and ContiguousContainer. Library is licensed under zlib license.

Using range-based for loop with a third-party container

I'm currently using a third-party library which contains a class that only provides indexed lookup, i.e. operator[].
I'd like to do a range-based for loop on this class's contents. However, having never written an iterator or iterator adapter, I'm quite lost. It seems that writing iterators is not a straightforward task.
My desired usage is:
for(auto element : container)
{
...
}
Instead of having to write:
for(int i = 0; i < container.size(); ++i)
{
auto element = container[i];
...
}
How can this be achieved? Does Boost provide this functionality?
Writing iterators is actually a rather straightforward task, but it gets extremely tedious. Since your container supports indexing by an integer, I assume its iterators would fall into the random access iterator category (if they existed). That needs a lot of boilerplate!
However, to support the range-based for loop, all you'll need is a forward iterator. We'll write a simple wrapper for the container that implements the forward iterator requirements, and then write two functions Iterator begin(Container&) and Iterator end(Container&) that enable the container to be used in the range-based for loop.
This Iterator will contain a reference to the container, the size of the container, and the current index within that container:
template<template<typename> class C, typename T>
class indexer : public std::iterator<std::forward_iterator, T>
{
public:
indexer(C<T>& c, std::size_t i, std::size_t end)
: c_(std::addressof(c)), i_(i), end_(end) {}
T& operator*() const {
return c_[i_];
}
indexer<C, T>& operator++() {
++i_;
return *this;
}
indexer<C, T> operator++(int) {
auto&& tmp = *this;
operator++();
return tmp;
}
bool operator==(indexer<C, T> const& other) const {
return i_ == other.i_;
}
bool operator!=(indexer<C, T> const& other) const {
return !(*this == other);
}
private:
C<T>* c_;
std::size_t i_, end_;
};
Inheriting from std::iterator conveniently declares the appropriate typedefs for use with std::iterator_traits.
Then, you would define begin and end as follows:
template<typename T>
indexer<Container, T> begin(Container<T>& c) {
return indexer<Container, T>(c, 0, c.size());
}
template<typename T>
indexer<Container, T> end(Container<T>& c) {
auto size = c.size();
return indexer<Container, T>(c, size, size);
}
Switch out Container for whatever the type of container is in your example, and with that, your desired usage works!
The requirements and behavior of all the various kinds of iterators can be found in the tables of section 24.2.2 of the standard, which are mirrored at cppreference.com here.
A random-access iterator implementation of the above, along with a demo of usage with a simple vector_view class can be found live on Coliru or ideone.com.
You can do the following:
1) define your own iterator It that contains, inside, a ref ref to your container container and an index i. When the iterator is dereferenced, it returns ref[i] by reference. You can write it yourself or you can use boost for help, it has an iterator library to easily define your own iterators. Constructor should accept a container& and a size_t. You can make also the const_iterator if this concept applies.
2) When operator++ is invoked on one iterator, it simply does ++i on the internal member. operator== and operator!= should simply compare i. You can assert, for security, that both iterators are coherent, that means their refs point to the same object.
3) add begin and end to your container class or, if this is not possible, define a global begin and end that accept your container& c. begin should simply return It(c, 0). end should return It(c, c.size()).
There could be a problem copying the iterators as they contain a reference and some other minor details, but I hope the overall idea is clear and correct.

typeid for container STL

I am writing a little template class which can get as template parameter list or vector (and the data type obviously).
I need to overload [ ] operator, to do this I want to use the overloaded [ ] of the vector and make a simple search (next, next, until we get to the desired index) for the list.
So I am checking with typeid if parameter is parameter of list and implements according to result like this:
const T* operator[](size_t _index)const
{
if(typeid(ContainerT<T,std::allocator<T> >) == typeid(vector<T>))
{
return m_container[_index];
}
else
{
const_iterator it = m_container.begin();
for(int i=0;i<_index;++i)
{
++it;
}
return *it;
}
}
If I dont use the [ ] for the list, everything is ok, but as I use it:
tContainer_t<int, list> list1;
cout<<list1[0]<<endl;
it is not compiling at all and here is the compilation error:
In file included from main.cpp:6:0:
tContainer.h: In member function ‘const T* tContainer_t<T, ContainerT>::operator[](size_t) const [with T = int, ContainerT = std::list, size_t = unsigned int]’:
main.cpp:68:9: instantiated from here
tContainer.h:80:29: error: no match for ‘operator[]’ in ‘((const tContainer_t<int, std::list>*)this)->tContainer_t<int, std::list>::m_container[_index]’
I dont understand since I checked that the typeid does work (well I think...) and anyway it seems the compiler sees that subscript will also be called for list.
Since the compiler needs to compile the entire function, even though one branch will not be taken at runtime (due to check), then you can't use this type of runtime checking to prevent the compilation error.
There are ways to do this involving using a helper function that can be specialized for the type. However, it can be complicated and in this case, not worth doing yourself, as the standard library has already done the work for you.
std::advance (located in header <iterator>) can be used to advance an iterator up to N times and is optimized for random-access iterators (like those returned by std::vector or std::deque) to do it in constant-time. Otherwise it falls back to steping one at a time using ++
// Note, changed signature to return const T&, which is more standard for operator[]
const T& operator[](size_t index) const
{
const_iterator itr = m_container.begin();
std::advance(itr, index);
return *itr;
}
Edit:
Assuming you wanted to learn how it's done, you would create the following functions, usually in a separate namespace. For now, I'm going to use the original intent of the question, and assume that you're using std::list or std::vector.
namespace helper
{
template <typename T, typename Alloc>
typename std::vector<T,Alloc>::const_reference
index_into(std::vector<T, Alloc> const& container, std::size_t index)
{
return container[index];
}
template <typename T, typename Alloc>
typename std::list<T,Alloc>::const_reference
index_into(std::list<T, Alloc> const& container, std::size_t index)
{
std::list<T, Alloc>::const_iterator itr = container.begin();
for(std::size_t i = 0; i < index; ++i)
{
++itr;
}
return *itr;
}
}
// Change your definition here
const T& operator[](size_t index) const
{
return helper::index_into(m_container, index);
}
Why this works: When you compile this with either std::list or std::vector, it uses function overloading to determine which of the two overloads of index_into to use. Hence, it only compiles the one that is legal, and doesn't attempt to compile both.
Note this, implementation only allows std::vector and std::list. If you wanted to allow any container, using std::advance is the generic and correct solution.
FYI, the implementation of std::advaance uses a similar technique. You can look at your implementation to see how it's done.
Use std::advance from <iterator>:
const T* operator[](size_t index) const
{
const_iterator it = m_container.begin();
std::advance(it, index);
return &*it;
}

Is the at() const accessor for map standard in C++11?

I was trying to figure out how to return a value from a map in a const method and I stumbled on the at() method for map in gcc 4.6.
When I looked this up I realized it was non-standard:
C++ map access discards qualifiers (const)
But it sure is a whole lot less verbose than the find() approach. I was wondering if the C++11 has rectified this - is at() for map part of the new standard?
Yes. std::map has an at member function in C++11 with the following specification (23.4.4.3/9):
T& at(const key_type& x);
const T& at(const key_type& x) const;
Returns: A reference to the mapped_type corresponding to x in *this.
Throws: An exception object of type out_of_range if no such element is present.
Complexity: logarithmic.
Note however that this member function has been added specifically to std::map. It is not required by the more general associative container requirement. If you are writing generic code that requires some associative container type, you can't use this new at. Instead, you should continue to use find, which is part of the associative container concept, or write your own non-member helper:
template <typename AssociativeContainer>
typename AssociativeContainer::mapped_type&
get_mapped_value(AssociativeContainer& container,
typename AssociativeContainer::key_type const& key)
{
typename AssociativeContainer::iterator it(container.find(key));
return it != container.end() ? it->second : throw std::out_of_range("key");
}
template <typename AssociativeContainer>
typename AssociativeContainer::mapped_type const&
get_mapped_value(AssociativeContainer const& container,
typename AssociativeContainer::key_type const& key)
{
typename AssociativeContainer::const_iterator it(container.find(key));
return it != container.end() ? it->second : throw std::out_of_range("key");
}
Or, if you have an implementation that supports rvalue references and decltype, you don't need two overloads:
template <typename AssociativeContainer, typename Key>
auto get_mapped_value(AssociativeContainer&& container, Key const& key)
-> decltype(std::declval<AssociativeContainer>().begin()->second)&
{
auto const it(container.find(key));
return it != container.end() ? it->second : throw std::out_of_range("key");
}
(Or something close to that; one fun thing about C++11 is that no two compilers have the same bugs and all seem to accept slightly different subsets of valid--and invalid--C++11 code.)