为地图/矢量等实现不可复制不可移动包装器

问题描述 投票:0回答:1

我想在没有复制或移动构造函数的 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>;

即使经过此改进,我们仍然存在指针切片的缺点。

我想不出有什么可以在没有指针切片缺点的情况下提供友好的使用。任何建议都会有帮助。

c++ templates generics stl c++14
1个回答
0
投票

您可以为要在类上使用的所有方法声明转发方法,例如:

#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_.end();
  }

  auto end() const
  {
    return val_.end();
  }

  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));
  }

  template <typename ... Params>
  void insert(Params && ...v)
    requires requires (Params && ...v) { value_type{}.insert(std::forward<Params>(v)...); }
  {
    val_.insert(std::forward<Params>(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 << ", ";
    }
    NoCopyMove<std::map<int, int>> map;
    map.insert(map.end(), std::pair{1, 1});
}

只要您不尝试调用无效方法,即使该方法不存在于

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)); }
      |                                                   ^

https://godbolt.org/z/qE51sYEbE

© www.soinside.com 2019 - 2024. All rights reserved.