17

考虑以下类。

struct with_copy {
    with_copy() = default;
    with_copy(with_copy const&) {}
    with_copy& operator=(with_copy const&) { return *this; }
};

struct foo {
    with_copy c;
    std::unique_ptr<int> p;
};
  • with_copy有复制构造函数吗?是的。它是明确定义的。
  • with_copy有移动构造函数吗?不会。显式复制构造函数会阻止生成它。
  • 是否with_copy有已删除的移动构造函数?不,没有移动构造函数与删除构造函数不同。已删除的移动构造函数将尝试移动格式错误而不是退化为副本。
  • 可复制吗with_copy?是的。它的复制构造函数用于复制。
  • with_copy可移动的吗?是的。它的复制构造函数用于移动。

...现在是棘手的。

  • foo有复制构造函数吗?是的。它有一个已删除的,因为由于调用unique_ptr的已删除复制构造函数,它的默认定义格式不正确。
  • foo有移动构造函数吗?GCC 说是,clang 说不。
  • 是否foo有已删除的移动构造函数?GCC 和 clang 都说不。
  • 可复制吗foo?不,它的复制构造函数被删除了。
  • foo可移动的吗?GCC 说是,clang 说不。

(当考虑分配而不是构造时,行为是相似的。)

据我所知,GCC 是正确的。foo应该有一个移动构造函数,对每个成员执行移动,在这种with_copy情况下会退化为副本。Clang 的行为似乎很荒谬:我有一个有两个可移动成员的聚合体,但我的聚合体是一块不可移动的砖。

谁是对的?

4

2 回答 2

8

C++11,或者更确切地说是 n3485,[class.copy]/9:

如果类的定义X没有显式声明移动构造函数,当且仅当

  • X没有用户声明的复制构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的移动赋值运算符,
  • X没有用户声明的析构函数,并且
  • 移动构造函数不会被隐式定义为已删除。

和 /11:

隐式声明的复制/移动构造函数是inline public其类的成员。如果具有以下条件,则类的默认复制/移动构造函数X被定义为已删除(8.4.3)X

  • [...]
  • 对于复制构造函数,右值引用类型的非静态数据成员,或
  • 对于移动构造函数,非静态数据成员或直接或虚拟基类,其类型不具有移动构造函数且不可轻易复制。

由于with_copy不是简单可复制的,因此没有移动构造函数(它将被定义为已删除,因此不会被隐式声明)foo


C++1y,或者更确切地说是 github repo 从 2013-11-12 提交 e31867c0;包含DR1402

/9:

如果类的定义X没有显式声明移动构造函数,当且仅当

  • X没有用户声明的复制构造函数,
  • X没有用户声明的复制赋值运算符,
  • X没有用户声明的移动赋值运算符,并且
  • X没有用户声明的析构函数。

和 /11:

隐式声明的复制/移动构造函数是inline public 其类的成员。如果具有以下条件,则类的默认复制/移动构造函数X 被定义为已删除(8.4.3)X

  • [...]
  • 对于复制构造函数,右值引用类型的非静态数据成员。

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

在这里,foo会有一个move-constructor。

于 2013-12-16T11:02:42.567 回答
7

我不太确定你测试了什么,但它foo肯定既可以移动分配又可以移动构造。诚然,这并没有说明移动构造函数或移动赋值是可访问的,只是来自右值的构造或赋值有效。clang ( clang version 3.5 (trunk 196718)) 和gcc (gcc version 4.9.0 20131031 (experimental) (GCC)) 都同意这个评估。这是我尝试过的完整来源:

#include <iostream>
#include <type_traits>
#include <memory>

struct with_copy {
    with_copy() = default;
    with_copy(with_copy const&) {}
    with_copy& operator=(with_copy const&) { return *this; }
};

struct foo {
    with_copy c;
    std::unique_ptr<int> p;
};

int main()
{
    std::cout << "move constructible: "
              << std::is_move_constructible<foo>::value << '\n';
    std::cout << "move assignable: "
              << std::is_move_assignable<foo>::value << '\n';
    foo f0;
    foo f1 = std::move(f0);
    f0 = std::move(f1);
}
于 2013-12-16T10:51:30.660 回答