为什么不能编译以下程序?
注意:something_t的move构造函数也不例外。
#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;
}
错误是(在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,
| ^~~~~
(与clang和MSVC几乎相同)。
如果我用“ this line”注释替换该行,std::unique_ptr<int> move_only
则代码可以正常编译:
struct data_t {
something_t something;
std::unique_ptr<int> move_only;
};
为什么要取消std::vector
帮助?std::vector
如果我将something_t
move构造函数设置为noexcept,它也可以编译,无论是否编译。
注意:noexcept
在something_t
的move构造函数中添加会有所帮助,但这不是问题。
问题是:
为什么这样:
struct data_t {
something_t something;
std::unique_ptr<int> move_only;
};
该程序可以编译吗?
但是随着
struct data_t {
something_t something;
std::vector<std::unique_ptr<int>> move_only; // <-- this line
};
程序不编译吗?
实际上,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
唯一的区别是:
is_copy_constructible: true false
即,data_t
withvector
是可复制构造的,而withunique_ptr
不是。但是这种差异如何影响编译?
这里的重要区别是:
std::is_copy_constructible<std::vector<std::unique_ptr<int>>>::value == true
std::is_copy_constructible<std::unique_ptr<int>>::value == false
第一个也许令人惊讶。但是请注意,is_copy_constructible
大多数相似的类型特征仅要求声明要测试的操作,而不是实际使用将是有效的。std::vector
不幸的是,这里缺少一些“ SFINAE正确性”,但这可能是为了向后兼容。
该标准template <class T, class Allocator> class vector
在[vector.overview] / 2中的描述只是说它声明了一个member vector(const vector& x);
。以下各节对复制构造函数没有其他说明。特别是,std::vector
在[optional.ctor] / 6中没有关于此语句的类似内容std::optional<T>
:
constexpr optional(const optional& rhs);
备注:除非
is_copy_constructible_v<T>
为true,否则此构造函数应定义为delete 。
由于对的各种要求std::vector<T>
,其功能(如push_back
,insert
)以及emplace
需要处理使用矢量中已有元素重新分配和填充新内存的可能性的功能必须这样实现:
std::is_nothrow_move_constructible<T>::value
为true,则使用的move构造函数T
,并且这些函数提供强大的异常保证。std::is_nothrow_move_constructible<T>::value
为false且std::is_copy_constructible<T>::value
为true,则使用的副本构造函数T
,并且这些函数提供强大的异常保证。std::is_nothrow_move_constructible<T>::value
和std::is_copy_constructible<T>::value
均为false,则使用的move构造函数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
使用move构造函数从上面的列表中选择第三个选项,但如果确实抛出,则向量将保持未指定状态。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句