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

时间:2015-02-12 11:32:44

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

我不是在寻找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开始{ {1}}提供了一个移动构造函数(请参阅here (8)),并将创建的临时值移入传递给std::basic_stringf的参数中。

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

f_c

但是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? } 没有移动任何 1 :它只将lvalues转换为rvalues引用,如果目标类型没有实现移动语义:没有移动操作是执行...以及上面std::move函数中的AFAIK std::move阻止编译器执行RVO(我们正在恶化代码!)。

因此,在(可能不必要的)介绍之后,我会问我的问题:

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

这个问题涉及基本类型和复杂类型:

return_by_value_x

  • 基本类型可以移动吗?有一些对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?
  • 具有const成员的复杂类型无法移动,不是吗?
    • 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
  • 如何自己测试上述情况,以确定给定类型是否在给定的上下文中移动?

1 如果它被称为f_weird({1, .0f, {0.d, 1.d}, {{{"a", 0ull}, {1u, 2u}}}}) // this moves?或类似的东西会不会更好?

1 个答案:

答案 0 :(得分: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

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

  

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

     
      
  • 可能构造的子对象类型M(或其数组)由于重载解析(13.3)而无法移动[...],如   应用于M的相应构造函数,导致歧义或   默认情况下删除或无法访问的功能   构造函数, [..]
  •   
     

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

用于初始化

Test(Test())

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