Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ generic insert into std container?

If I have the following program:

#include <vector>
#include <set>

template<class T, class U>
void AddToContainer(T& container, U value)
{
  container.push_back(value);
}

int main(char**, int)
{
   std::vector<int> v;
   AddToContainer(v, 1);

   std::set<int> s;
   AddToContainer(s, 1);

   return 0;
}

How can I make the adding to the container generic? Since std::set hasn't got a push_back but only insert, this will fail to compile.

like image 264
paulm Avatar asked May 22 '16 19:05

paulm


People also ask

What is containers in C++ STL?

Containers in C++ STL (Standard Template Library) Last Updated: 12-07-2020 A container is a holder object that stores a collection of other objects (its elements).

What is a container in Java?

A container is a holder object that stores a collection of other objects (its elements). They are implemented as class templates, which allows a great flexibility in the types supported as elements.

How do you insert a string in C with CSTR?

Syntax 3: Inserts the characters of the C-string cstr so that the new characters start with index idx. string& string::insert (size_ type idx, const char* cstr) idx : is the index number where insertion is to be made. *cstr : is the pointer to the C-string which is to be inserted.

Why are containers implemented as class templates in Java?

They are implemented as class templates, which allows great flexibility in the types supported as elements. The container manages the storage space for its elements and provides member functions to access them, either directly or through iterators (reference objects with similar properties to pointers).


2 Answers

You could use expression SFINAE with a dummy parameter to check if push_back() works:

template <class C, class V>
auto append(C& container, V&& value, int)
    -> decltype(container.push_back(std::forward<V>(value)), void())
{
    container.push_back(std::forward<V>(value));
}

template <class C, class V>
void append(C& container, V&& value, ...)
{
    container.insert(std::forward<V>(value));
}

which your function will just forward to:

template <class C, class V>
void AddToContainer(C& container, V&& value) {
    append(container, std::forward<V>(value), 0);
}

If push_back() is a valid expression, the first overload will be preferred since int is a better match for 0 than ... If push_back() isn't a valid expression, then there's only one viable overload.


Whether this is actually a good idea or not is a separate question.

like image 177
Barry Avatar answered Sep 28 '22 09:09

Barry


I believe that all* C++ containers (though not the container adapters like priority_queue) have a version of insert that looks like this:

iterator insert(iterator location, T&& value)

For the sequence collections, the location is the actual location; for associative collections (like map and unordered_map), the iterator is a "hint" parameter (for instance, to help a map insert an element quickly if you already know precisely where it belongs in sorted order). However, providing an invalid hint doesn't cause any invalid behavior, so a valid generic insert for C++ collections would be:

template<C, T>
void insert(C& collection, T&& value) {
    collection.insert(collection.end(), std::forward<T>(value));
}

* It looks like forward_list is the only one that doesn't have this method, which makes sense.

like image 27
Lucretiel Avatar answered Sep 28 '22 10:09

Lucretiel