如何知道或测试给定类型是否要移动

How to know or test if a given type is going to be moved

本文关键字:类型 是否 移动 何知道 测试      更新时间:2023-10-16

我不是在为可移动类型寻找类型特征,也不是在寻找自动生成移动操作的规则。我正在寻找的是一般指南,以了解给定类型是否将被移动或复制,或者一种通过测试自己弄清楚的方法。

在某些情况下,移动操作是在用户不会注意到的情况下执行的,例如:

void f(std::string) { ... }
void f_c(const std::string) { ... }
void g()
{
    f(std::string("Hello world!"));   // moved, isn't it?
    f("Hello world!");                // moved, isn't it?
    f_c(std::string("Hello world!")); // moved, isn't it?
    f_c("Hello world!");              // moved, isn't it?
}

在 C++11 之前,上面的代码将产生从临时值到传递给 ff_cstd::string副本,从 C++11 开始,std::basic_string提供了一个移动构造函数(见这里 (8((,临时创建的代码被移动到传递给 ff_c 的参数中。

有时用户试图使用 std::move 函数强制移动语义:

std::string return_by_value_1()
{
    std::string result("result);
    return std::move(result); // Is this moved or not?
}
std::string return_by_value_2()
{
    return std::move(std::string("result)); // Is this moved or not?
}

但是std::move不会移动任何东西1:如果目标类型没有实现移动语义,它只会将左值转换为右值引用:不执行移动操作......上述return_by_value_x函数中的 AFAIK std::move会阻止编译器执行 RVO(我们正在恶化代码!

所以,在(也许不必要的(介绍之后,我会问我的问题:

如何知道或测试给定类型是否将被移动或复制?

这个问题是关于基本类型和复杂类型的:

int f_int(int) { ... };
template <typename F, typename S> void f_pair(std::pair<F, S>) { ... };
struct weird
{
    int i;
    float f;
    std::vector<double> vd;
    using complexmap = std::map<std::pair<std::string, std::uint64_t>, std::pair<std::uint32_t, std::uint32_t>>;
    complexmap cm;
};
struct silly
{
    std::vector<std::pair<const std::string, weird::complexmap>> vcm;
};
f_weird(weird) { ... };
f_silly(silly) { ... };
<小时 />
  • 基本类型可以移动吗? 有一些对f_int的调用,这意味着移动操作?
    • f_int(1); // this moves or construct an int in-place?
    • f_int(1 + 2); // this moves or construct an int in-place?
    • f_int(f_int(1) + 2); // this moves or construct an int in-place?
  • 具有常量成员的复杂类型无法移动,不是吗?
    • f_pair<std::pair<const std::string, int>>({"1", 2}); // unmovable?
    • f_pair<std::pair<std::string, std::string>>({"1", "2"}); // this moves?
    • f_silly({{{}, {}}}); // this moves?
  • struct weird是可移动的吗?
    • f_weird({1, .0f, {0.d, 1.d}, {{{"a", 0ull}, {1u, 2u}}}}) // this moves?
  • 如何自己测试上述情况以确定给定类型是否在给定上下文中移动?
<小时 />

1如果它被称为std::rvalref或类似的东西不是更好吗?

如何自己测试上述情况以确定是否给定 类型是否在给定上下文中移动?

您可以测试派生类是否会将默认的移动构造函数定义为通过 SFINAE 删除。这个想法不适用于final类。一个似乎有效的粗略草图是

namespace detail {
  // No copy constructor. Move constructor not deleted if T has an applicable one.
  template <typename T>
  struct Test : T { Test(Test&&) = default; };
}
// TODO: Implement unions
template <typename T>
using is_moveable = std::is_move_constructible<
    std::conditional_t<std::is_class<T>{}, detail::Test<T>, T>>;

演示

Test 的移动构造函数是根据 [dcl.fct.def.default]/5 的默认移动构造函数。使上述工作的相关引用在 [class.copy]/11 中:

X的默认 [..] 移动构造函数定义为 删除 (8.4.3( 如果X有:

  • 一个潜在的构造子对象类型M(或其数组(,由于重载分辨率 (13.3( 而无法移动 [..],作为 应用于M的相应构造函数,导致歧义或 从默认值中删除或无法访问的函数 构造函数, [..]

定义为已删除的默认移动构造函数将被重载解析 (13.3、13.4( 忽略。

对于初始化

Test(Test())

若要有效,由于复制 CTOR 不是隐式声明的,因此移动构造函数必须可用。 但是,根据上面的引用,如果基类(我们给定的T(没有可调用的移动构造函数,则Test的移动构造函数将被定义为已删除(因此被重载解析忽略(。(我有点不确定T没有任何移动构造函数的情况,我将不胜感激,因为那里的标准得到了澄清。