我想在没有复制或移动构造函数的 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_;
};
上面可以用任何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;
};
上述内容很容易受到指针切片的影响,因为 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>;
即使经过此改进,我们仍然存在指针切片的缺点。
我想不出有什么可以在没有指针切片缺点的情况下提供友好的使用。任何建议都会有帮助。
您可以为要在类上使用的所有方法声明转发方法,例如:
#include <utility>
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)...) {}
auto begin()
{
return val_.begin();
}
auto begin() const
{
return val_.begin();
}
auto end()
{
return val_.begin();
}
auto end() const
{
return val_.begin();
}
template <typename Value>
void push_back(Value && v)
{
val_.push_back(std::forward<Value>(v));
}
private:
NoCopyMove(const NoCopyMove&) = delete;
NoCopyMove(NoCopyMove&&) = delete;
value_type val_;
};
#include <vector>
#include <map>
#include <iostream>
int main()
{
NoCopyMove<std::vector<int>> vec;
vec.push_back(10);
for (auto i : vec)
{
std::cout << i << ", ";
}
}
只要您不尝试调用无效方法,即使该方法不存在于
value_type
中,它也会编译。例如:
NoCopyMove<std::map<int, int>> map;
编译但是:
map.push_back(std::pair{1, 1});
会产生错误:
<source>:36:10: error: no member named 'push_back' in 'std::map<int, int>'
36 | val_.push_back(std::forward<Value>(v));
| ~~~~ ^
<source>:58:9: note: in instantiation of function template specialization 'NoCopyMove<std::map<int, int>>::push_back<std::pair<int, int>>' requested here
58 | map.push_back(std::pair{1, 1});
| ^
如果你可以使用c++20,你可以用概念让错误更直接一些:
template <typename Value>
void push_back(Value && v)
requires requires (Value && v) { value_type{}.push_back(std::forward<Value>(v)); }
{
val_.push_back(std::forward<Value>(v));
}
map.push_back
现在将给出:
<source>:58:9: error: no matching member function for call to 'push_back'
58 | map.push_back(std::pair{1, 1});
| ~~~~^~~~~~~~~
<source>:33:8: note: candidate template ignored: constraints not satisfied [with Value = std::pair<int, int>]
33 | void push_back(Value && v)
| ^
<source>:34:51: note: because 'value_type{}.push_back(std::forward<Value>(v))' would be invalid: no member named 'push_back' in 'std::map<int, int>'
34 | requires requires (Value && v) { value_type{}.push_back(std::forward<Value>(v)); }
| ^