为什么既没有使用clang声明也没有删除移动构造函数?

时间:2013-12-16 10:33:53

标签: c++ c++11 user-defined-functions move-semantics implicit-declaration

考虑以下类别。

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说没有。

(当考虑赋值而不是构造时,行为类似。)

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

谁是对的?

2 个答案:

答案 0 :(得分:8)

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

  

如果类X的定义没有显式声明一个移动构造函数,那么将隐式声明一个   当且仅当

时默认为      
      
  • X没有用户声明的复制构造函数,
  •   
  • X没有用户声明的副本分配运算符
  •   
  • X没有用户声明的移动分配运算符
  •   
  • X没有用户声明的析构函数,
  •   
  • 移动构造函数不会被隐式定义为已删除。
  •   

和/ 11:

  

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

     
      
  • [...]
  •   
  • 表示复制构造函数,rvalue引用类型的非静态数据成员,或
  •   
  • 用于移动构造函数,非静态数据成员或类型为的直接或虚拟基类   没有移动构造函数,并且不是轻易复制的。
  •   

由于with_copy 可以轻易复制,foo将有移动构造函数(它将被定义为已删除,因此它赢了不能隐含声明。


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

/ 9:

  

如果类X的定义未明确声明移动   构造函数,只有当一个隐式声明为默认值   如果

     
      
  • X没有用户声明的复制构造函数,
  •   
  • X没有用户声明的副本分配运算符
  •   
  • X没有用户声明的移动赋值运算符和
  •   
  • X没有用户声明的析构函数。
  •   

和/ 11:

  

隐式声明的复制/移动构造函数是inline public   同类的成员。类X的默认复制/移动构造函数   如果X有:

,则定义为已删除(8.4.3)      
      
  • [...]
  •   
  • 表示复制构造函数,是rvalue引用类型的非静态数据成员。
  •   
     

被忽略的默认移动构造函数被忽略   重载决议(13.3,13.4)。

在这里,foo将有一个移动构造函数。

答案 1 :(得分:7)

我不太确定你测试的是什么,但它foo肯定都是可分配的并且可以移动构造。不可否认,这并没有说明移动构造函数或移动赋值是否可访问,只是来自右值的构造或赋值有效。 clang(clang版本3.5(主干196718))和gcc(gcc版本4.9.0 20131031(实验)(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);
}