有没有办法在提供左值和右值重载的同时删除重复代码?
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_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)
..?[=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&
。相反,如果发送一个右值,它会推导出 int
为 U
所以参数是 int &&
.
此模式称为转发引用。
如您所见,如果转发参数存在模板参数推导,则此类代码只有 属性。
请注意,如果未正确约束,将其设为模板将使其接受比预期更多的类型。
您也可以使用复制然后移动来避免代码重复:
inline constexpr void insert(const T &v) {
T copy = v;
insert(std::move(copy)); // calls the rvalue overload
}
在学习 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_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)
..?[=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&
。相反,如果发送一个右值,它会推导出 int
为 U
所以参数是 int &&
.
此模式称为转发引用。
如您所见,如果转发参数存在模板参数推导,则此类代码只有 属性。
请注意,如果未正确约束,将其设为模板将使其接受比预期更多的类型。
您也可以使用复制然后移动来避免代码重复:
inline constexpr void insert(const T &v) {
T copy = v;
insert(std::move(copy)); // calls the rvalue overload
}