仅在继承的构造函数

时间:2016-05-06 05:34:45

标签: c++ gcc c++14 move-semantics

以下代码不会编译GCC 6.1,但适用于Clang 3.8.0Visual Studio 2015

#include <memory>

class base {
public:
    base(std::unique_ptr<int>) {}
};

class derived : public base {
public:
    using base::base;
};

int main() {
    derived df(std::make_unique<int>());
}

出现以下错误:

main.cpp: In constructor 'derived::derived(std::unique_ptr<int>)':

main.cpp:10:17: error: use of deleted function 
'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) 
[with _Tp = int; _Dp = std::default_delete<int>]'

     using base::base;

                 ^~~~

In file included from /usr/local/include/c++/6.1.0/memory:81:0,

                 from main.cpp:1:

/usr/local/include/c++/6.1.0/bits/unique_ptr.h:356:7: note: declared here

       unique_ptr(const unique_ptr&) = delete;

       ^~~~~~~~~~

main.cpp: In function 'int main()':

main.cpp:14:39: note: synthesized method 'derived::derived(std::unique_ptr<int>)' 
first required here 

     derived df(std::make_unique<int>());

它似乎试图调用已删除的复制构造函数,但这很好用:

void foo(std::unique_ptr<int>) {}

int main() {
    foo(std::make_unique<int>());
}

此示例-fno-elide-constructors打印出move called.

struct move_only {
    move_only() { std::cout << "default called."; }
    move_only(move_only&&) { std::cout << "move called."; }
};

void foo(move_only) { }

int main() {
    foo(move_only{});
}

我意识到这两种情况并不完全相同,但似乎很奇怪,&&需要使继承的构造函数示例编译而不是后者。作为一个完整性检查,明确地执行move_only(const move_only&) = delete;并将签名更改为void foo(const move_only&) { }仍然可以编译,除非这次甚至没有调用移动构造函数(也许是elision)。

最新标准草案的

12.6.3说:

  

1调用类型B的构造函数来初始化对象时   不同类型的D(即,继承构造函数时)   ([namespace.udecl])),初始化就好像是默认的   默认构造函数用于初始化D对象和每个基础   继承构造函数的class子对象,除了   通过调用继承来初始化B子对象   构造函数。完整的初始化被认为是单一的   功能调用;特别是继承的初始化   构造函数的参数在任何初始化之前被排序   D对象的一部分。 [示例:

struct B1 {
  B1(int, ...) { }
};

// ...

struct D1 : B1 {
  using B1::B1;  // inherits B1(int, ...)
  int x;
  // ...
};

void test() {
  D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4),
                 // then d.x is default-initialized (no initialization is performed),
  // ...
}

// ...

那么它应该完全等同于foo(move_only)对吗?

1 个答案:

答案 0 :(得分:3)

这似乎是一个错误(报告为bug 70972)。 N4140 [class.inhctor] / 8:

  

隐式定义的继承构造函数执行set of   由用户编写的类的初始化   该类的内联构造函数,带有 mem-initializer-list   只有 mem-initializer 有一个 mem-initializer-id ,用于命名基数    using-declaration 嵌套名称说明符中表示的类   以及下面指定的表达式列表,以及   函数体中的复合语句为空(12.6.2)。如果说   用户编写的构造函数将是格式错误的,程序是   病态的。 表达式列表中的每个表达式都是表单   static_cast<T&&>(p),其中p是相应的名称   构造函数参数和Tp的声明类型。

换句话说,这里讨论的继承构造函数应该是移动而不是复制它的参数。