I want to write a wrapper over the STL containers like map, vector, unordered map etc. that do not have copy or move constructors. There are a few approaches that I can think of, but none are nice:
Approach 1: Use templates:
// Define a template NoCopyMove which can be instantiated with STL container types.
template <typename V>
struct NoCopyMove {
public:
using value_type = V;
value_type& get() { return val_; }
template <typename... Args>
NoCopyMove(Args&&... args): val_(std::forward<Args>(args)...) {}
private:
NoCopyMove(const NoCopyMove&) = delete;
NoCopyMove(NoCopyMove&&) = delete;
value_type val_;
};
The above can be instantiated with any STL container and then the container can be accessed using the get() function
Approach 2: Use Public Inheritance:
template <typename Key,
typename T,
typename Hash = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>>
class unordered_map_ncm
: public std::unordered_map<Key, T, Hash, KeyEqual, Allocator> {
// Inherit constructors.
using std::unordered_map<Key, T, Hash, KeyEqual, Allocator>::unordered_map;
private:
unordered_map_ncm(const unordered_map_ncm&) = delete;
unordered_map_ncm(unordered_map_ncm&&) = delete;
};
The above is susceptible to pointer slicing because STL containers do not have virtual constructors and has to be done for each STL type. The upside is that we get to use STL-like functions without the .get() call in the template-based approach.
Approach 2': The above can be generalized further so that instead of a map, any STL type can be used as follows:
template <template <typename...> class T, typename... Us>
class NoCopyMove : public T<Us...> {
public:
using T<Us...>::T;
private:
NoCopyMove(const NoCopyMove&) = delete;
NoCopyMove(NoCopyMove&&) = delete;
};
template<typename Key,
typename T,
typename Hash = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>>
using unordered_map_ncm = NoCopyMove<std::unordered_map, Key, T, Hash, KeyEqual, Allocator>;
Even after this improvement, we have the drawback of pointer slicing.
I cannot think of anything which gives a friendly usage without the pointer slicing drawback. Any suggestions would be helpful.
It is better to privately inherit from the container - than to have it as private member - because we can use
use Base::xxxx;to get types and methods from it.And of course - delete copy constructor and assign operator (move will be deleted too that way).
If we close our eyes on that we cannot prevent from copying element one by one
NoCopy<std::vector<int>> v; std::vector<int> a(v.begin(), a.end());- then this should work:Now, we can check it works as expected: