右值引用 Cpp17MoveConstructible 吗?

问题描述 投票:0回答:1

右值引用是否满足 Cpp17MoveConstructible 要求?

为此,在表达式

T u = rv;
中,
u
需要“等价”于
rv
。如果
T
是右值引用(例如
int&&
),那么在
int&& u = 0;
中,
u
需要“等效”于
0
。然而,引用是否等同于一个值,还是仅仅引用一个等同的对象?

动机

我问的原因是因为我不清楚

std::swap<int&&>
应该如何表现。

is_move_constructible_v<int&&>
is_move_assignable_v<int&&>
为真,因此满足
std::swap
的约束并且可以实例化。 但是,如果
swap<int&&>
不满足
Cpp17MoveConstructible
,则 int&& 的效果未定义。

事实上,所有主要实现(libc++、libstdc++、MSVC STL)都按照以下方式实现

std::swap(a, b)

T t = std::move(a);
a = std::move(b);
b = std::move(t);

如果

T
是右值引用,则
T t = _MOVE(a)
不会创建移动构造的副本,而是将一个右值引用绑定到另一个右值引用。 对于
T = int&&
,效果相当于:

a = a;
b = a;

我们对此有何看法?

  • Rvalue 引用不满足 Cpp17MoveConstructible。所有实现都是正确的。
    swap
    的前提条件不满足所以可以这样。
  • 右值引用满足Cpp17MoveConstructible。所有的实现都是错误的。

嗯,是哪一个?

c++ reference language-lawyer swap rvalue-reference
1个回答
0
投票

在我看来,右值引用满足 Cpp17MoveConstructibleCpp17MoveAssignable,因此满足

std::swap
的先决条件。

但是,

std::swap<int&&>(a, b)
不会交换两个
int
变量。完整的示例是here

#include <type_traits>
#include <cstdio>


template <class T>
requires std::is_move_constructible_v<T> && std::is_move_assignable_v<T>
constexpr void swap(T& a, T& b)
    noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T>)
{
    T tmp = std::move(a);
    a     = std::move(b);
    b     = std::move(tmp);
}


int main()
{
    int a = 1, b = 2;

    swap<int&&>(a, b);

    std::printf("a = %d, b = %d.\n", a, b);
}

原因是,

swap
的第一行应该将
a
的值存储在临时变量中,但
T
int&&
,它不是值类型。所以,这个初始化不会移动任何东西,它只是创建一个别名。

我认为,您使用

std::swap
是一种非常罕见的方法。通常模板参数是推导出来的,因此它永远不是引用类型。

不过,我们必须承认,标准中的Effects并没有得到满足,因此这是所有实现中的一个错误。虽然,不是太严重。他们应该使用

std::decay_t<T>
auto
作为
tmp
的类型。

© www.soinside.com 2019 - 2024. All rights reserved.