为什么下面的程序不能编译?
注意:something_t 的移动构造函数不是 noexcept。
#include <memory>
#include <vector>
class something_t {
public:
constexpr something_t() = default;
constexpr something_t(const something_t& other)
: field_(other.field_) {
}
constexpr something_t(something_t&& other)
: field_(other.field_) {
}
private:
unsigned int field_{ 0 };
};
struct data_t {
something_t something;
std::vector<std::unique_ptr<int>> move_only; // <-- this line
};
int main() {
std::vector<data_t> result;
data_t data;
result.push_back(std::move(data));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
错误是(在 g++ 中):
/usr/include/c++/9/bits/stl_uninitialized.h:127:72: error: static assertion failed: result type must be constructible from value type of input range
127 | static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
| ^~~~~
Run Code Online (Sandbox Code Playgroud)
(几乎与 clang 和 MSVC 相同)。
如果我用“这一行”注释替换该行,std::unique_ptr<int> move_only则代码编译正常:
struct data_t {
something_t something;
std::unique_ptr<int> move_only;
};
Run Code Online (Sandbox Code Playgroud)
为什么删除有std::vector帮助?std::vector如果我将something_t移动构造函数设置为 noexcept,它也可以编译或不编译。
注意:添加noexcepttosomething_t的移动构造函数有帮助,但这不是问题。
问题是:
为什么这样:
struct data_t {
something_t something;
std::unique_ptr<int> move_only;
};
Run Code Online (Sandbox Code Playgroud)
程序能编译吗?
但是随着
struct data_t {
something_t something;
std::vector<std::unique_ptr<int>> move_only; // <-- this line
};
Run Code Online (Sandbox Code Playgroud)
程序不能编译吗?
事实上,两者std::unique_ptr<int>和std::vector<std::unique_ptr<int>>:
所以它们具有相同的属性。
更新:我尝试比较两种变体的 type_traits:
data_t(vector) data_t(unique_ptr):
is_constructible: true true
is_trivially_constructible: false false
is_nothrow_constructible: true true
is_default_constructible: true true
is_trivially_default_constructible: false false
is_nothrow_default_constructible: true true
is_copy_constructible: true false
is_trivially_copy_constructible: false false
is_nothrow_copy_constructible: false false
is_move_constructible: true true
is_trivially_move_constructible: false false
is_nothrow_move_constructible: false false
is_assignable: false false
is_trivially_assignable: false false
is_nothrow_assignable: false false
is_copy_assignable: false false
is_trivially_copy_assignable: false false
is_nothrow_copy_assignable: false false
is_move_assignable: false false
is_trivially_move_assignable: false false
is_nothrow_move_assignable: false false
is_destructible: true true
is_trivially_destructible: false false
is_nothrow_destructible: true true
is_swappable: false false
is_nothrow_swappable: false false
Run Code Online (Sandbox Code Playgroud)
唯一的区别是:
is_copy_constructible: true false
Run Code Online (Sandbox Code Playgroud)
即,data_twithvector是可复制构造的,而 withunique_ptr则不是。但是这种差异如何影响编译呢?
这里的重要区别是:
std::is_copy_constructible<std::vector<std::unique_ptr<int>>>::value == true
std::is_copy_constructible<std::unique_ptr<int>>::value == false
Run Code Online (Sandbox Code Playgroud)
第一个可能令人惊讶。但请注意,is_copy_constructible大多数相似的类型特征只要求声明它们测试的操作,而不是实际使用它是有效的。std::vector不幸的是,这里缺少一些“SFINAE 正确性”,但这可能是为了向后兼容。
的标准的描述template <class T, class Allocator> class vector中[vector.overview / 2简单地说,它声明了一个成员vector(const vector& x);。以下各节没有提及复制构造函数。特别是,std::vector没有类似于[optional.ctor]/6 中关于复制构造函数的这句话的一段std::optional<T>:
Run Code Online (Sandbox Code Playgroud)constexpr optional(const optional& rhs);备注:除非
is_copy_constructible_v<T>为真,否则此构造函数应定义为已删除。
由于对 的各种要求std::vector<T>,它的函数如push_back、insert和emplace需要处理重新分配和填充向量中已有元素的新内存的可能性,被迫像这样实现:
std::is_nothrow_move_constructible<T>::value为真,则使用 的移动构造函数T,并且函数提供强异常保证。std::is_nothrow_move_constructible<T>::value为假且std::is_copy_constructible<T>::value为真,则使用 的复制构造函数T,函数提供强异常保证。std::is_nothrow_move_constructible<T>::value和std::is_copy_constructible<T>::value都为假,则使用 的移动构造函数T,但函数不能提供强异常保证。(T必须是可移动构造的,这实际上可能意味着使用复制构造函数,作为这些容器函数的一般要求。)
因此,当data_t有std::vector<std::unique_ptr<int>>成员时,它“错误地”具有一个未删除的隐式声明的复制构造函数。这导致std::vector<data_t>::push_back从上面的列表中选择第二个选项,但实际使用复制构造函数会导致错误。
当data_t有std::unique_ptr<int>成员时,其删除的复制构造函数意味着隐式声明的复制构造函数data_t也被删除。所以在这种情况下,std::vector<data_t>::push_back从上面的列表中选择第三个选项,使用移动构造函数,但如果它抛出,向量将处于未指定状态。
| 归档时间: |
|
| 查看次数: |
414 次 |
| 最近记录: |