c++ - 为什么派生类中复制/移动函数的 "normal"实现会根据它们的定义方式表现不同?

标签 c++ c++11 constructor-overloading universal-reference

我对派生类复制和移动函数调用其基类版本时看到的行为感到困惑。

我有一个包含各种构造函数的基类,这些构造函数会在它们被调用时告诉我:

#include <iostream>

class Base {
public:     
  Base() {}

  template<typename T>
  Base(T&&) { std::cout << "URef ctor\n"; }

  Base(const Base&) { std::cout << "Copy ctor\n"; }

  Base(Base& rhs): Base(const_cast<const Base&>(rhs))
  { std::cout << "  (from non-const copy ctor)\n"; }

  Base(Base&&) { std::cout << "Move ctor\n"; }

  Base(const Base&& rhs): Base(rhs)
  { std::cout << "  (from const move ctor)\n"; }
};

对于具有编译器生成的复制和移动操作的派生类

class Derived: public Base {};

和这个测试代码,

int main()                              
{                                      
  Derived d;                         
  Derived copyNCLValue(d);          
  Derived copyNCRvalue(std::move(d)); 

  const Derived cd;
  Derived copyCLValue(cd);      
  Derived copyCRvalue(std::move(cd));    
}

gcc 4.8.1 产生这个输出:

Copy ctor
Move ctor
Copy ctor
Copy ctor

这让我很吃惊。我希望采用通用引用的基类构造函数被调用,因为它可以被实例化以在可能从派生类的函数传递的派生对象上创建精确匹配。基类复制和移动函数需要派生到基的转换。

如果我更改派生类以自己声明复制和移动函数,但为它们提供默认实现,

class Derived: public Base {
public:
  Derived(){}

  Derived(const Derived& rhs) = default;
  Derived(Derived&& rhs) = default;     
};

gcc 产生相同的输出。但是如果我自己用我认为是默认实现的函数编写函数,

class Derived: public Base {
public:
  Derived(){}

  Derived(const Derived& rhs): Base(rhs) {}
  Derived(Derived&& rhs): Base(std::move(rhs)) {}
};

我得到了我最初预期的输出:

URef ctor
URef ctor
URef ctor
URef ctor

我希望在每种情况下得到相同的输出。这是 gcc 中的错误,还是我不理解的地方?

最佳答案

This surprises me. I expected the base class constructor taking the universal reference to be called, because it can be instantiated to create an exact match on the derived object that is presumably passed from the derived class's functions. The base class copy and move functions require a derived-to-base conversion.

没有。编译器看到行 Derived copyCRvalue(std::move(cd));这真的意味着 Derived copyCRvalue(static_cast<const Derived&&>(cd));它试图在 Derived 中找到一个构造函数与那个电话相匹配。它找到两个密切相关的构造函数,它们都隐式声明:

Derived(Derived const &); // copy constructor
Derived(Derived &&);      // move constructor

不能使用第二个,因为右值引用指向 const对象,但第一个是匹配项。隐式定义的复制构造函数的定义是:

Derived(Derived const &rhs) : base(static_cast<Base const &>(rhs)) {}

在构造函数内部,rhs是一个左值,而不是一个右值(模板构造函数无论如何都不是复制构造函数)。

But if I write the functions myself with what I believe are the default implementations,

class Derived: public Base {
public:
  Derived(){}

  Derived(const Derived& rhs): Base(rhs) {}
  Derived(Derived&& rhs): Base(std::move(rhs)) {}
};

除了那些不是编译器将提供的定义。具体配额来自标准12.8/15

The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.

也就是说,隐式定义的构造函数将使用源的基数初始化目标的基数,并且类似地,目标中的每个成员都使用源中的相同成员。

关于c++ - 为什么派生类中复制/移动函数的 "normal"实现会根据它们的定义方式表现不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17932210/

相关文章:

c++ - 模板类成员 VS 嵌套类前向声明

C# 构造函数重载

javascript - 如何在 JavaScript ECMA6 中重载构造函数?

c++ - 什么是 undefined reference /未解析的外部符号错误,我该如何解决?

c++ - 如何在 SuperpixelSLIC 中找到段的唯一标签

c++ - 复制包含 Eigen::CholmodDecomposition 的自定义类对象时出现“使用已删除函数”错误

c++ - 如何将 lambda 作为成员函数的函数指针参数传递?

c++ - 'TypeInfo<char>(char * )' isn' t defined but worked pre-C++11; what changed, and how can I fix the error?

c++ - 为什么这个构造函数重载不起作用? (发生模板和类型定义)

c++ - 为使用Little或Big字节序的计算机编写程序。并且有相同的结果