use*_*983 23 c++ code-duplication move-semantics c++11
C++ 11"移动"是一个很好的功能,但我发现当与"复制"同时使用时,很难避免代码重复(我们都讨厌这个).下面的代码是我实现的一个简单的循环队列(不完整),两个push()方法几乎相同,除了一行.
我遇到过很多像这样的情况.任何想法如何避免这种代码重复而不使用宏?
===编辑===
在这个特定的例子中,重复的代码可以被重构并放入一个单独的函数中,但有时这种重构是不可用的或者不能轻易实现.
#include <cstdlib>
#include <utility>
template<typename T>
class CircularQueue {
public:
CircularQueue(long size = 32) : size{size} {
buffer = std::malloc(sizeof(T) * size);
}
~CircularQueue();
bool full() const {
return counter.in - counter.out >= size;
}
bool empty() const {
return counter.in == counter.out;
}
void push(T&& data) {
if (full()) {
throw Invalid{};
}
long offset = counter.in % size;
new (buffer + offset) T{std::forward<T>(data)};
++counter.in;
}
void push(const T& data) {
if (full()) {
throw Invalid{};
}
long offset = counter.in % size;
new (buffer + offset) T{data};
++counter.in;
}
private:
T* buffer;
long size;
struct {
long in, out;
} counter;
};
Run Code Online (Sandbox Code Playgroud)
bol*_*lov 14
这里最简单的解决方案是使参数成为转发参考.这样你就可以只使用一个函数:
template <class U>
void push(U&& data) {
if (full()) {
throw Invalid{};
}
long offset = counter.in % size;
// please note here we construct a T object (the class template)
// from an U object (the function template)
new (buffer + offset) T{std::forward<U>(data)};
++counter.in;
}
Run Code Online (Sandbox Code Playgroud)
但是方法有一些缺点:
它不是通用的,也就是说它不能总是这样做(以微不足道的方式).例如,当参数不像T(例如SomeType<T>)那么简单时.
您延迟参数的类型检查.当使用错误的参数类型调用push时,可能会出现长且看似无关的编译器错误.
顺便说一下,在你的例子T&&中不是转发参考.这是一个右值参考.那是因为T不是函数的模板参数.这是类,因此在实例化类时已经推断出它.所以编写代码的正确方法是:
void push(T&& data) {
...
... T{std::move(data)};
...
}
void push(const T& data) {
... T{data};
...
}
Run Code Online (Sandbox Code Playgroud)