【问题标题】:How to know or test if a given type is going to be moved如何知道或测试给定类型是否会被移动
【发布时间】:2015-02-12 11:32:44
【问题描述】:

我不是在寻找type trait for movable types,也不是rules for automatic generation of move operations。我正在寻找的是一个通用指南,以了解是否要移动或复制给定类型,或者通过测试自己确定它的方法。

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

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 之前,上面的代码会在 std::string 中从一个临时值复制到传递给 ff_c 的值,从 C++11 开始,std::basic_string 提供了一个移动构造函数(见here (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:它只将左值转换为右值引用,如果目标类型没有实现移动语义:不执行移动操作......和 ​​AFAIK上面return_by_value_x 函数中的std::moveing 会阻止编译器执行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?
  • 不能移动具有 const 成员的 complex 类型,不是吗?
    • f_pair&lt;std::pair&lt;const std::string, int&gt;&gt;({"1", 2}); // unmovable?
    • f_pair&lt;std::pair&lt;std::string, std::string&gt;&gt;({"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或者类似的名字会不会更好?

【问题讨论】:

  • ...你知道ff_c的签名是完全一样的吗?
  • 这似乎是一个大约 25 部分的问题。你能每个问题问一个问题吗?
  • 我认为这里没有真正的问题。
  • 也许不能通过右值构造?
  • @LightnessRacesinOrbit 也许你是对的,在我看来所有的问题都是一样的(所有的问题都是关于移动语义的)但也许每个部分都值得一个单独的问题,这就是你建议?

标签: c++ c++11 move-semantics


【解决方案1】:

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

您可以测试派生类是否会将默认移动构造函数定义为通过 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>>;

Demo.

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

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

  • 一个潜在构造的子对象类型M(或其数组)由于重载决议(13.3)无法[..]移动,如 应用于M 的相应构造函数,导致歧义或 从默认设置中删除或无法访问的功能 构造函数, [..]

重载决议(13.3、13.4)忽略定义为已删除的默认移动构造函数。

用于初始化

Test(Test())

为了有效,因为没有隐式声明复制 ctor,移动构造函数必须可用。然而,根据上面的引用,如果我们给定的T 基类没有可调用的移动构造函数,Test 的移动构造函数将被定义为已删除(因此被重载决议忽略)。 (我有点不确定T 没有任何移动构造函数的情况,我会很感激那里的标准澄清。)

【讨论】:

    猜你喜欢
    • 2018-08-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 2019-04-08
    • 2021-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多