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