【问题标题】:What does the definition(body) of an inherited constructor look like?继承的构造函数的定义(主体)是什么样的?
【发布时间】:2015-03-13 12:25:24
【问题描述】:

根据我对 SO 和 cppreference link 的答案的阅读

继承的构造函数等价于用户定义的构造函数,其主体为空,成员初始化列表由单个 nested-name-specifier 组成,它将其所有参数转发给基类构造函数。

我得出的结论是,以下类 DE 应该具有相同的行为。

#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&amp;&amp;。但是,D d = foo() 运行良好。这是为什么? 使用的编译器是clang3.5。

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

【问题讨论】:

    标签: c++ c++11 inheriting-constructors


    【解决方案1】:

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

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

    (这与 E(string&amp;&amp;) 形成对比,其中命名的 a 参数不再被认为是传递给 B::B 的 xvalue(临时到期)。)

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

    【讨论】:

    • 这个定义会是什么样子?这个问题只有一个例子。但是,如果说 D 有其他成员具有默认构造函数怎么办?当 cppreference 链接说 forward 时,它实际上是否暗示使用通用参考 + std::forward 成语?如果是这样,它将如何解决here 概述的问题?
    • “但是定义会是什么样子?” - 它不会...您所做的任何心理近似都是有缺陷的,如“此处概述的问题”中所述您刚刚发布的链接提到了构造函数的明确性问题。我建议您不要将派生构造函数心理建模为调用基类析构函数的 C++ 代码,而是将暴露的基类构造函数视为可通过派生类接口访问,并具有一些潜在的隐藏前置和/或后置步骤初始化其他基础和成员、VDT 等。
    【解决方案2】:

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

    来自 N3337,§12.9/8 [class.inhctor]

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

    因此构造函数参数被完美地转发到相应的继承构造函数(std::forward§20.2.3)被指定返回static_cast&lt;T&amp;&amp;&gt;(p),与上面的描述完全相同)。根据声明的构造函数参数类型,引用折叠发生,如this answer 中所述。

    在你的情况下,[T=string&amp;&amp;] 和演员再次产生string&amp;&amp;,它可以绑定到B 的参数。为了匹配E 中的行为,您应该将构造函数重写为

    E(string&& a) : B(static_cast<string&&>(a))
    {
    }
    

    【讨论】:

    • 对不起,如果我遗漏了一些明显的东西,但标准引用的强调部分听起来与问题中的 link 认为不足的方法相同。
    • @Pradhan 是不够的,如果您按照该描述并实现转发构造函数,则需要手动将其标记为explicit,这与继承显式(如果有)的 using 声明不同基础构造函数。该标准在将继承的构造函数的行为与用户提供的构造函数的行为进行比较时没有解决这个问题。
    猜你喜欢
    • 2012-04-16
    • 2013-11-30
    • 2023-03-18
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    相关资源
    最近更新 更多