有没有办法在提供左值和右值重载的同时删除重复代码?

Is There A Way To Remove Duplicate Code While Providing lvalue and rvalue Overloads?

在学习 C++ 的过程中,我决定编写一个简单的模板化二叉搜索树 (bst),但遇到了以下问题:我希望能够 construct 一个 bst,同时传递它左值 const T &val 和右值 T &&val。同样,我希望能够 insert 左值和右值。所以我得到了很多我不喜欢的重复代码:

/// copy constructor
explicit inline constexpr binary_search_tree(const T &val)
    : _root{std::make_unique<binary_search_tree_node>(val)} {}

/// move constructor
explicit inline constexpr binary_search_tree(T &&val)
    : _root{std::make_unique<binary_search_tree_node>(std::move(val))} {}

对于构造函数,其中 binary_search_tree_nodebinary_search_tree 的私有成员,然后还必须提供复制和移动构造函数:

struct binary_search_tree_node {
  T value;
  std::unique_ptr<binary_search_tree_node> left;
  std::unique_ptr<binary_search_tree_node> right;

  // prohibit creation of tree_node without value
  inline constexpr binary_search_tree_node() = delete;

  /// copy constructor
  explicit inline constexpr binary_search_tree_node(const T &val)
      : value{val}, left{nullptr}, right{nullptr} {}

  /// move constructor
  explicit inline constexpr binary_search_tree_node(T &&val)
      : value{std::move(val)}, left{nullptr}, right{nullptr} {}
};

还有:

inline constexpr void insert(const T &v) {
  if (!_root) {
    _root = std::make_unique<binary_search_tree_node>(v);
    ++_size;
  } else {
    insert(_root, v);
  }
}

inline constexpr void insert(T &&v) {
  if (!_root) {
    _root = std::make_unique<binary_search_tree_node>(std::move(v));
    ++_size;
  } else {
    insert(_root, std::move(v));
  }
}

用于插入函数。

当我想搜索一个值时,这个列表会继续下去:我应该为 find(const T &val) find(T &&val)..?[=21= 提供重载吗]

So my question is whether there is a way of combining these overloads or any other way to remove this duplicate code?

我读到 reference collapsing rules 但我不确定我是否可以在这里利用这个概念。

任何其他想法或建议也将不胜感激。

是的,您可以使用引用折叠来减少编写的函数量。

例如,您的函数 insert 可以使用右值引用 + 直接上下文来利用引用折叠,从而产生转发引用:

template<typename U>
inline constexpr void insert(U &&v) { // v is a forwarding reference
    if (!_root) {
        // we use std::forward to keep rvalue-ness
        // of the named object when v is an rvalue, but not when it's a lvalue
        _root = std::make_unique<binary_search_tree_node>(std::forward<U>(v));
        ++_size;
    } else {
        insert(_root, std::forward<U>(v));
    }
}

即使参数使用右值引用,左值也会在这里工作,因为引用折叠。如果 U 推导为 int&,则参数类似于 int& &&,它会折叠为 int&。相反,如果发送一个右值,它会推导出 intU 所以参数是 int &&.

此模式称为转发引用。

如您所见,如果转发参数存在模板参数推导,则此类代码只有 属性。

请注意,如果未正确约束,将其设为模板将使其接受比预期更多的类型。

您也可以使用复制然后移动来避免代码重复:

inline constexpr void insert(const T &v) {
    T copy = v;
    insert(std::move(copy)); // calls the rvalue overload
}