2

从我阅读关于 SO 和cppreference 链接的答案

继承的构造函数等效于用户定义的构造函数,其主体为空,成员初始化列表由单个嵌套名称说明符组成,它将所有参数转发给基类构造函数。

我得出的结论是,下面的类D应该E表现得一致。

#include <string>
#include <utility>
using namespace std;

class B
{
public:
  B(string&& a) : a(move(a))
  {
  }

  string a;
};

class D : public B
{
public:
  using B::B;
};

class E : public B
{
public:
  E(string&& a) : B(a)
  {
  }
};

string foo()
{
  return "bar";
}

int main()
{
  D d = foo();//This compiles
  E e = foo();//This does not compile
  return 0;
}

E e = foo()正确地无法编译,因为B的构造函数只接受string&&. 但是,D d = foo()通过罚款。这是为什么?使用的编译器是clang3.5。

编辑:另外,正如在这个答案中所解释的,完美的转发习语不能替代继承构造函数。那么,身体究竟是什么样子的呢?

4

2 回答 2

2

但是, D d = foo() 运行良好。这是为什么?

因为using B::B有效地将临时字符串直接传递给B' 的构造函数,它仍然可以绑定 ala &&(即它的值类别仍然是xvalue),然后进行任何额外的派生类初始化(如果有其他数据成员,VDT 等)。这是非常可取的,因为使用基类构造函数的目的是允许相同的客户端使用。

(这与E(string&&), 其中命名a参数不再被视为 xvalue (过期临时)成熟以传递给B::B.)

(如果您还没有,您可能还想看看(std::forward)[ http://en.cppreference.com/w/cpp/utility/forward] ...它有助于完美地转发参数)

于 2015-01-15T06:45:07.780 回答
2

标准中关于派生类中继承的构造函数定义的措辞比 cppreference 描述所暗示的更明确,但后者中的关键短语是转发其所有参数。换句话说,参数的值类别被保留,您的定义E没有这样做,因此无法编译。

从 N3337,§12.9/8 [class.inhctor]

...隐式定义的继承构造函数执行类的一组初始化,这些初始化将由用户编写的内联构造函数为该类执行,该类具有一个mem-initializer-list,其唯一的mem-initializer具有一个mem-initializer-id命名在using-declaration的nested-name-specifier和下面指定的表达式列表中表示的基类,并且其函数体中的复合语句为空 (12.6.2)。如果该用户编写的构造函数格式错误,则程序格式错误。表达式列表中的每个表达式的形式为,其中static_cast<T&&>(p)p是相应的构造函数参数的名称,并且T是 的声明类型p

因此构造函数参数被完美地转发给相应的继承构造函数(std::forward§20.2.3)被指定为 return static_cast<T&&>(p),与上面的描述完全相同)。根据构造函数参数的声明类型,会发生引用折叠,如本答案中所述。

在您的情况下,强制转换再次[T=string&&]产生string&&,可以绑定到B's 参数。为了匹配该行为,E您应该将构造函数重写为

E(string&& a) : B(static_cast<string&&>(a))
{
}
于 2015-01-15T07:10:30.853 回答