【问题标题】:C++11 field of rvalue is a rvalue右值的 C++11 字段是右值
【发布时间】:2017-11-28 21:21:46
【问题描述】:

我正在尝试设置一个模板来交换数组,以便在许多类的移动构造函数中使用:

template <class T> void tools_swap_array(T* & a, T* & b) noexcept
{
    auto tmp(a);
    a = b;
    b = tmp;
}

现在我想在移动构造函数中使用它,并通过一个名为 move_from() 的通用方法移动赋值运算符:

void label::move_from(label && ref) noexcept  
{
    tools_swap_array((char *)val, (char*)ref.val);
}

其中 val 是类标签的字段:

class label
{
public:
    // [...]
    label(label && ref) { move_from(std::move(ref)); };
    label & operator = (label && ref) { move_from(std::move(ref)); return *this; };
    // [...]
private:
    char val[LABEL_SIZE];
};

但是当我运行编译器时,它在 move_from() 中失败,告诉 ref.val 是一个右值

error: invalid initialization of non-const reference of type 'char*&'
from an rvalue of type 'char*'  
tools_swap_array((char *)val, (char*)ref.val);
                                            ^

我认为“label && ref”将是 move_from() 中的左值,“ref.val”也是如此。那么,我是否仍然误解了 C++11 对右值的引用,或者这是一个编译器错误?

感谢任何提示!

【问题讨论】:

  • 使用(char *)val 进行强制转换会生成char* 类型的rvalue(临时对象),它指向与val 相同的地址。非常量引用不能引用右值。编译器说(char*)ref.val 是一个右值,而不是ref.val
  • 除此之外,交换两个指向任意大小数组开头的指针根本不会修改所述数组。
  • 在 c++ 中编程时停止使用原始指针。这些是为罕见的例外情况保留的,这些情况很可能已经用 c++ 标准库中的工具解决了。
  • @user0042 使用原始指针本身并没有错。您应该避免的是在所有权的情况下使用原始指针。另请参阅 Cpp 核心指南的 R.3。
  • 我了解 std::unique_ptr 和 std::shared_ptr,但这是旧代码 (C++98) 我正在对 C++14 进行一些更新,主要是为了尽可能多地利用move semantics ...但我不会全部重写:)

标签: c++ rvalue-reference


【解决方案1】:

实现这一点的正确方法是根本不编写自己的交换函数。 std::swap() 在 C++11 及更高版本中具有固定大小数组的重载,请改用它,例如:

#include <utility>

void label::move_from(label && ref) noexcept  
{
    std::swap(val, ref.val);
}

如果你真的想手动实现数组交换功能,你需要传递对实际数组的引用,而不是传递指向它们的指针(仅交换指针根本不会交换数组元素),例如:

#include <algorithm>

template <class T, std::size_t N>
void tools_swap_array(T (&a)[N], T (&b)[N]) noexcept
{
    T tmp[N];
    std::copy_n(a, N, tmp);
    std::copy_n(b, N, a);
    std::copy_n(tmp, N, b);
}

或者:

#include <utility>

template <class T, std::size_t N>
void tools_swap_array(T (&a)[N], T (&b)[N]) noexcept
{
    for(std::size_t i = 0; i < N; ++i)
        std::swap(a[i], b[i]);
}

或者(这基本上就是std::swap() 所做的):

#include <algorithm>

template <class T, std::size_t N>
void tools_swap_array(T (&a)[N], T (&b)[N]) noexcept
{
    std::swap_ranges(a, a+N, b);
}

无论哪种方式,您都可以这样做:

void label::move_from(label && ref) noexcept  
{
    tools_swap_array(val, ref.val);
}

【讨论】:

  • 谢谢雷米。我最初尝试像您一样设置我自己的交换模板而不使用 size 参数(使用 T (&amp;a) [] 作为参数),但显然它不起作用。所以我尝试使用指针并得到这个右值错误,这让我很惊讶。但是你是对的,没有必要定义我自己的交换方法,我将使用 std::swap 一个,感谢您指出它并感谢您详细说明了我可以实现自己的交换模板的原因和方式:)
猜你喜欢
  • 1970-01-01
  • 2013-07-12
  • 1970-01-01
  • 1970-01-01
  • 2011-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多