通用参考:无法将参数从'int'转换为'int &&'。

4

我正在使用VS 2015社区版运行以下所有代码。

当我尝试实施Code Review中建议我的建议时,我的代码出现错误。我遇到问题的部分是将参数更改为TryPush(T&& val)

#pragma once

#include <atomic>
#include <memory>


template <typename T> class RingBuffer {
public:

   /*
   Other functions
   */

    void Push(T val) {
        while (!TryPush(val));
    }

private:

   /*
   Other functions
   */

    //Private Member Functions
    bool TryPush(T && val) {
        const std::size_t current_write = write_position.load(std::memory_order_acquire);
        const std::size_t current_read = read_position.load(std::memory_order_acquire);
        const std::size_t next_write = increment_index(current_write);

        if (next_write == current_read) { return false; }

        _ring_buffer_array[current_write] = std::move(val);
        write_position.store(next_write, std::memory_order_release);

        return true;
    }

    std::size_t increment_index(std::size_t index) {
        return (index + 1) % _buffer_capacity;
    }

    //Private Member Variables
    std::atomic<std::size_t> read_position = 0;
    std::atomic<std::size_t> write_position = 0;

    std::size_t _buffer_capacity;
    std::unique_ptr<T[], RingBufferFree> _ring_buffer_array;
};

每当我尝试编译这段代码时,都会出现以下错误:bool RingBuffer::TryPush(T &&)': cannot convert argument 1 from 'int' to 'int &&。令我困惑的是,如果我将代码更改为

#pragma once

#include <atomic>
#include <memory>


template <typename T> class RingBuffer {
public:

   /*
   Other functions
   */

    void Push(T && val) {
        while (!TryPush(val));
    }

private:

   /*
   Other functions
   */

    //Private Member Functions
    bool TryPush(T val) {
        const std::size_t current_write = write_position.load(std::memory_order_acquire);
        const std::size_t current_read = read_position.load(std::memory_order_acquire);
        const std::size_t next_write = increment_index(current_write);

        if (next_write == current_read) { return false; }

        _ring_buffer_array[current_write] = std::move(val);
        write_position.store(next_write, std::memory_order_release);

        return true;
    }

    std::size_t increment_index(std::size_t index) {
        return (index + 1) % _buffer_capacity;
    }

    //Private Member Variables
    std::atomic<std::size_t> read_position = 0;
    std::atomic<std::size_t> write_position = 0;

    std::size_t _buffer_capacity;
    std::unique_ptr<T[], RingBufferFree> _ring_buffer_array;
};

它编译并运行。我从Scott Meyer的博客文章中得出的印象是TryPush(T && val)是一个通用引用,我应该能够像第一个代码片段中所示那样使用它,然后将值移动到数组中,从而确保代码能够正常工作,无论传递给函数的是左值还是右值。如果它是公共的Push方法,它似乎可以工作,因此我有点困惑发生了什么。我一定是漏掉了什么,想知道是否有人能够澄清具体情况。谢谢。 编辑 这样调用。
RingBuffer<int> r(50);
for (int i = 0; i < 20; i++) {
    r.Push(i + 1);
}

你如何称呼 TryPush 函数? - NathanOliver
1个回答

6

您的代码中没有通用引用。在您提供的博客文章中,可以看到一个类似的例子:

template <class T, class Allocator = allocator<T> >
class vector {
public:
    ...
    void push_back(T&& x);       // fully specified parameter type ⇒ no type deduction;
    ...                          // && ≡ rvalue reference
};
要使用这段代码,您可以写以下内容:vector<int> v; v.push_back(x);,函数已知采用int&&,因此不需要推断。
通用引用仅在从参数中推断模板类型时发生(并且它们起作用是因为类型可以被推断为引用类型)。
如果您将TryPush(val)更改为TryPush(std::move(val)),则原始代码(通过值传递)将正常工作。为了消除不必要的移动操作,您可以提供两个重载,例如:
    void Push(T && val)     { while (!TryPush(std::move(val))); }
    void Push(T const& val) { while (!TryPush(val)); }
private:
    template<typename U>
    bool TryPush(U&& val)
    {
       // preparation logic...
        _ring_buffer_array[current_write] = std::forward<U>(val);

您可以当然使用两个重载函数 T const &T&& 代替通用引用来实现 TryPush,但这会导致代码在两个主体之间出现重复。
此外,您甚至可以用以下方式替换 Push:
template<typename U>
void Push(U&& val)
{
    while ( !TryPush(std::forward<U>(val)) ); 
}

这是您建议的设置方式吗?有两个公共方法 void Push(T && val)void Push(const T & val),它们在内部都调用 bool TryPush(T val)。这样做有什么问题吗? - cogle
@cogle 我扩展了这个例子。 - M.M
std::forward(val) 应该改为 std::forward<U>(val) 吗? - aschepler
感谢介绍forward,这是一种非常聪明的解决问题的方法。 - cogle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接