来自我对SO和cpp首选项链接的答案的阅读
继承的构造函数等效于具有空主体和成员初始值设定项列表的用户定义构造函数,该列表由单个嵌套名称说明符组成,该说明符将其所有参数转发给基类构造函数。
我得出的结论是,下面的类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。
编辑:此外,正如本答案中所解释的,完美的转发习惯用法不能替代继承构造函数。那么,身体到底是什么样子的呢?
但是,D d = foo() 可以正常通过。为什么?
因为using B::B
有效地将临时字符串直接传递给B
的构造函数,在那里它仍然可以绑定&&
(即它的值类别仍然是xvalue),然后进行任何额外的派生类初始化(如果有其他数据成员,VDT等)。 这是非常可取的,因为使用基类构造函数的目的是允许相同的客户端使用。
(这与 E(string&&)
形成鲜明对比,其中命名的 a
参数不再被视为 xvalue(即将过期的临时)可以传递给 B::B
。
如果你还没有,你可能也想看看(std::forward
)[http://en.cppreference.com/w/cpp/utility/forward]...它有助于完美转发参数)
标准中关于派生类中继承的构造函数定义的外观的措辞比 cppreference 描述所暗示的内容更明确一些,但后者中的关键短语是转发其所有参数。换句话说,参数的值类别被保留,而E
的定义不会这样做,因此无法编译。
来自 N3337, §12.9/8[class.inhctor]
...
隐式定义的继承构造函数执行类的初始化集,该初始化将由用户编写的内联构造函数为该类执行,该构造函数具有 mem-initializer-list,其唯一的 mem 初始值设定项具有mem-initializer-id,用于命名在 using-声明和表达式列表的嵌套名称说明符中表示的基类如下所述,并且其函数体中的复合语句为空 (12.6.2)。如果该用户编写的构造函数格式不正确,则程序格式不正确。表达式列表中的每个表达式都是static_cast<T&&>(p)
的形式,其中p
是相应构造函数参数的名称,T
是声明的p
类型。
因此,构造函数参数被完美地转发给相应的继承构造函数(std::forward
(§20.2.3)被指定返回static_cast<T&&>(p)
,与上面的描述完全相同)。根据构造函数参数的声明类型,引用折叠将按照本答案中所述发生。
在您的情况下,[T=string&&]
和强制转换再次产生string&&
,这可以绑定到B
的参数。若要在 E
中匹配该行为,应将构造函数重写为
E(string&& a) : B(static_cast<string&&>(a))
{
}