c++ - 使用 C++11 复制和 move 时避免代码重复

标签 c++ c++11 move-semantics code-duplication

C++11“move ”是一个不错的功能,但我发现当与“复制”同时使用时很难避免代码重复(我们都讨厌这个)。下面的代码是我实现的一个简单的循环队列(不完整),两个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;
};

最佳答案

这里最简单的解决方案是使参数成为转发引用。这样您就可以只使用一个功能:

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

虽然方法有缺点:

  • 它不是通用的,也就是说它不能总是完成(以微不足道的方式)。例如,当参数不像 T 那样简单时(例如 SomeType<T> )。

  • 你延迟了参数的类型检查。当使用错误的参数类型调用 push 时,可能会出现冗长且看似无关的编译器错误。


顺便说一下,在你的例子中 T&&不是转发引用。这是一个右值引用。那是因为 T 不是函数的模板参数。它属于类,因此在实例化类时已经推导出来。所以编写代码的正确方法是:

void push(T&& data) {
    ...
    ... T{std::move(data)};
    ...
}

void push(const T& data) {
   ... T{data};
   ...
}

关于c++ - 使用 C++11 复制和 move 时避免代码重复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34461376/

相关文章:

c++ - 结合完美转发模板和任意值类型模板

C++:文件、编码和数据类型

c++ - std::map 声明 - 错误的模板

c++ - 如何定义作为不可实例化类的静态成员的数组的大小?

rust - Rust 中的 move 语义

c++ - C++11 中的高效算术运算符重载

c++ - 用于创建对象时引用更改值

c++ - 为什么通过对 gcc 和 clang 的优化将大 double 转换为 uint16_t 会给出不同的答案

C++ Overload Operator = for Pointers 不能正常工作/编译

c++ - 如何使用外部链接在 namespace 范围内定义常量 double?