以下代码不会编译GCC 6.1
,但适用于Clang 3.8.0
和Visual 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)
对吗?
答案 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
是相应的名称 构造函数参数和T
是p
的声明类型。
换句话说,这里讨论的继承构造函数应该是移动而不是复制它的参数。