Ama*_*tam 5 c++ generics templates stl c++14
我想在没有复制或移动构造函数的 STL 容器(如地图、向量、无序地图等)上编写一个包装器。我能想到一些方法,但没有一个是好的:
方法一:使用模板:
// 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_;
};
Run Code Online (Sandbox Code Playgroud)
上面可以用任何STL容器实例化,然后可以使用get()函数访问该容器
方法 2:使用公共继承:
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;
};
Run Code Online (Sandbox Code Playgroud)
上面的内容很容易受到指针切片的影响,因为 STL 容器没有虚拟构造函数,并且必须对每个 STL 类型进行虚拟构造函数。好处是我们可以使用类似 STL 的函数,而无需.get()在基于模板的方法中进行调用。
方法 2':上面的内容可以进一步推广,以便可以使用任何 STL 类型来代替映射,如下所示:
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>;
Run Code Online (Sandbox Code Playgroud)
即使经过这样的改进,我们仍然存在指针切片的缺点。
我想不出任何可以在没有指针切片缺点的情况下提供友好使用的东西。任何的意见都将会有帮助。
最好从容器私有继承 - 比将其作为私有成员更好 - 因为我们可以使用use Base::xxxx;它来获取类型和方法。
当然 - 删除复制构造函数和赋值运算符(移动也将被删除)。
如果我们闭上眼睛,我们无法阻止一一复制元素NoCopy<std::vector<int>> v; std::vector<int> a(v.begin(), a.end());- 那么这应该有效:
namespace detail {
template <typename Base>
struct ContainerSpecific : Base {
using Base::Base;
};
}
template <typename Container>
struct ContainerNotCopyable : private detail::ContainerSpecific<Container>
// note: this is private ^^^^^^^ inheritance:
{
using Base = detail::ContainerSpecific<Container>;
using Base::Base;
ContainerNotCopyable(const ContainerNotCopyable&) = delete;
ContainerNotCopyable& operator=(const ContainerNotCopyable&) = delete;
// common stl containers types
using Container::size_type;
using Container::value_type;
using Container::iterator;
using Container::const_iterator;
using Container::reverse_iterator;
using Container::const_reverse_iterator;
// common stl containers methods
using Container::size;
using Container::empty;
using Container::begin;
using Container::rbegin;
using Container::cbegin;
using Container::end;
using Container::cend;
using Container::rend;
};
Run Code Online (Sandbox Code Playgroud)
namespace detail {
template <typename ...T>
struct ContainerSpecific<std::vector<T...>> : std::vector<T...> {
using Base = std::vector<T...>;
using Base::Base;
using Base::operator[];
using Base::at;
using Base::erase;
using Base::clear;
};
}
template <typename T, typename A=std::allocator<T>>
using VectorNotCopyable = ContainerNotCopyable<std::vector<T,A>>;
namespace detail {
template <typename T, std::size_t N>
struct ContainerSpecific<std::array<T, N>> : std::array<T, N> {
using Base = std::array<T, N>;
using Base::Base;
using Base::operator[];
using Base::at;
};
}
template <typename T, std::size_t N>
using ArrayNotCopyable = ContainerNotCopyable<std::array<T,N>>;
namespace detail {
template <typename ...T>
struct ContainerSpecific<std::map<T...>> : std::map<T...> {
using Base = std::map<T...>;
using Base::Base;
using Base::mapped_type;
using Base::key_type;
using Base::find;
using Base::emplace;
using Base::insert;
using Base::insert_or_assign;
using Base::try_emplace;
};
}
template <typename K, typename V, typename C=std::less<K>, typename A=std::allocator<std::pair<const K, V>>>
using MapNotCopyable = ContainerNotCopyable<std::map<K,V,C,A>>;
Run Code Online (Sandbox Code Playgroud)
现在,我们可以检查它是否按预期工作:
template <typename T>
constexpr bool has_no_copy_move =
!std::is_copy_constructible_v<T>
&& !std::is_copy_assignable_v<T>
&& !std::is_move_constructible_v<T>
&& !std::is_move_assignable_v<T>;
static_assert(has_no_copy_move<VectorNotCopyable<int>>);
static_assert(has_no_copy_move<ArrayNotCopyable<int, 7>>);
static_assert(has_no_copy_move<MapNotCopyable<int, int>>);
Run Code Online (Sandbox Code Playgroud)