Есть ли способ удалить дублирующийся код при предоставлении перегрузки lvalue и rvalue? - PullRequest
3 голосов
/ 18 февраля 2020

Во время изучения C ++ я решил написать простое шаблонное дерево двоичного поиска (bst) и столкнулся со следующей проблемой: я хочу иметь возможность конструировать bst, передавая ему значение lvalue типа const T &val и значение типа T &&val. Точно так же я хочу иметь возможность вставить lvalues ​​и rvalues. В итоге у меня появилось много дублирующегося кода, который мне не нравится:

/// 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_node - это закрытый член binary_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) ..?

Так что мой вопрос в том, есть ли способ объединить эти перегрузки или каким-либо другим способом удалить этот дублирующий код?

Я читал о правилах свертывания ссылок , но я не уверен, смогу ли я используйте эту концепцию здесь.

Любые другие мысли или предложения также приветствуются.

1 Ответ

5 голосов
/ 18 февраля 2020

Да, вы можете использовать свертывание ссылок, чтобы уменьшить объем написанной функции.

Например, ваша вставка функции может использовать ссылку rvalue + непосредственный контекст для использования свертывания ссылок, что приводит к пересылке ссылки:

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

Даже если параметр использует ссылку rvalue, lvalue будет работать здесь из-за свертывания ссылки. Если U выводит до int&, то параметр имеет вид int& &&, который сворачивается до int&. Напротив, если передается значение r, оно выводит int как U, поэтому параметр равен int &&.

Этот шаблон называется ссылкой для пересылки.

Когда вы Можно видеть, что такой код имеет это свойство только в том случае, если для параметра переадресации существует дедукция аргумента шаблона.

Имейте в виду, что если сделать его шаблоном, он будет принимать больше типов, чем предполагалось, если не ограничен правильно.

Вы также можете использовать копию и переместить ее, чтобы избежать дублирования кода:

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