1

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

我有一个带有各种构造函数的基类,它们告诉我何时调用它们:

#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 中的错误,还是我不理解的地方?

4

1 回答 1

6

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

不。编译器看到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)) {}

在构造函数内部, therhs是一个左值,而不是一个右值(模板化的构造函数也不是复制构造函数)。

但是,如果我自己用我认为是默认实现的方式编写函数,

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

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

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

非联合类 X 的隐式定义的复制/移动构造函数执行其基类和成员的成员复制/移动。

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

于 2013-07-29T19:15:16.503 回答