Move constructors of STL containers in MSVC 2017 are not marked as noexcept(MSVC 2017 中 STL 容器的移动构造函数未标记为 noexcept)
问题描述
我正在将我的项目从 VS2015 转移到 VS2017,这当然并不顺利.
I am moving my project from VS2015 to VS2017, which of course does not go smoothly.
我看到可以通过以下代码重现的奇怪编译器错误:
I am seeing strange compiler error that can be reproduced by the following code:
struct MoveOnly
{
MoveOnly() {}
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator = (const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
MoveOnly& operator = (MoveOnly&&) = default;
bool operator == (const MoveOnly& rhs)const{return false;}
};
struct Hasher
{
size_t operator()(const MoveOnly&)const{return 0;}
};
std::vector < std::unordered_map<MoveOnly, int, Hasher> > test;
test.emplace_back();
我可以使用所有编译器(gcc 7.2、clang 5.0.0、icc 18 以及 MSVC 2015)成功编译此代码.请点击此链接查看测试:https://godbolt.org/g/uSqwDJ.但是,在 MSVC 2017 (19.10.25017) 上,编译器尝试引用 MoveOnly
类型的已删除复制构造函数会导致错误.这个错误对我来说没有多大意义,因为没有理由在这里复制任何东西而不是移动./std:c++14
、/std:c++17
、/std:c++latest
没有帮助.gcc 和 clang 正确处理代码的事实也让我怀疑 msvc 2017 编译器.
I can successfully compile this code with all compilers (gcc 7.2, clang 5.0.0, icc 18, as well as MSVC 2015). Please follow this link to see the test:
https://godbolt.org/g/uSqwDJ. On MSVC 2017 (19.10.25017), however there is an error that is caused by the compiler trying to reference deleted copy constructor of MoveOnly
type. This error does not make much sense to me, because there is no reason to copy anything here instead of moving. /std:c++14
, /std:c++17
, /std:c++latest
do not help. Also the fact the gcc and clang handle the code correctly makes me suspicious about msvc 2017 compiler.
更新:
Yakk 发现问题所在后,我尝试使用其他容器代替 unordered_map
并且代码只能用 vector
编译.
After Yakk found what the problem is, I tried using other containers in place of unordered_map
and the code only compiles with vector
.
推荐答案
所以问题好像是这样的:
So the problem seems to be this:
static_assert( noexcept(std::unordered_map<MoveOnly, int, Hasher>( std::declval<std::unordered_map<MoveOnly, int, Hasher>&&>())), "");
您的编译器不认为 std::unordered_map<MoveOnly, int, Hasher>
可以是 noexcept
移动构造的.
Your compiler doesn't think std::unordered_map<MoveOnly, int, Hasher>
can be noexcept
move-constructed.
然后,MSVC 2017 附带的偏执 std::vector
实现依赖于复制元素,以在矢量调整大小时生成强大的异常保证.
Then, the paranoid std::vector
implementation that MSVC 2017 ships with falls back on copying elements to generate a strong exception guarantee on vector resize.
复制显然是不可能的.
现在 std::unordered_map<int, int>
也是如此——MSVC 认为移动它也有引发异常的风险;我相信您对 key 或 hash 类型所做的任何事情都不可能使 unordered_map
的移动构造函数异常安全.
Now the same is true of std::unordered_map<int, int>
-- MSVC thinks that moving it also risks throwing exceptions; I believe nothing you can do with the key or hash type can possibly make the move constructor of unordered_map
exception-safe.
unordered_map(unordered_map&&)
没有充分的理由不 是 noexcept
.我不确定标准是否允许或强制它,或者它是否是编译器中的错误;如果标准要求它是noexcept(false)
,那么标准就有缺陷.
There is no good reason for unordered_map(unordered_map&&)
to not be noexcept
. I am uncertain if the standard permits it or mandates it, or if it is a bug in the compiler; if the standard mandates it to be noexcept(false)
, then the standard has a defect.
您可以通过存储 unique_ptr
的向量来解决此问题.或者编写一个吃异常的 value_ptr
,对代码的更改较少:
You can work around this by storing a vector of unique_ptr
s. Or write an exception-eating value_ptr
with fewer changes to your code:
template<class T>
struct value_ptr {
std::unique_ptr<T> raw;
value_ptr() noexcept(true)
{
try {
raw = std::make_unique<T>();
} catch (...) {}
}
template<class T0, class...Ts,
std::enable_if_t<!std::is_same<value_ptr, std::decay_t<T0>>{}, bool> =true
>
value_ptr(T0&& t0, Ts&&...ts) noexcept(true)
{
try {
raw=std::make_unique<T>( std::forward<T0>(t0), std::forward<Ts>(ts)... )
} catch(...) {}
}
value_ptr(value_ptr&& o)noexcept(true)=default;
value_ptr(value_ptr const& o)noexcept(true)
{ try {
if (o.raw)
raw = std::make_unique<T>(*o.raw);
}catch(...){}
}
value_ptr& operator=(value_ptr&& o)noexcept(true)=default;
value_ptr& operator=(value_ptr const& o)noexcept(true)
{ try {
if (o.raw)
raw = std::make_unique<T>(*o.raw);
}catch(...){}
return *this;
}
T* operator->() const { return raw.get(); }
T& operator*() const { return *raw; }
explicit operator bool() const { return (bool)raw; }
};
这是我的测试工具:
template<class M>
void test_M() {
static_assert( noexcept(M( std::declval<M&&>())), "");
std::vector < M > test;
test.emplace_back();
}
void foo()
{
using M0=value_ptr<std::unordered_map<MoveOnly, int, Hasher>>;
using M1=std::unique_ptr<std::unordered_map<MoveOnly, int, Hasher>>;
test_M<M0>();
test_M<M1>();
}
这篇关于MSVC 2017 中 STL 容器的移动构造函数未标记为 noexcept的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:MSVC 2017 中 STL 容器的移动构造函数未标记为 noexcept
基础教程推荐
- 如何使图像调整大小以在 Qt 中缩放? 2021-01-01
- 管理共享内存应该分配多少内存?(助推) 2022-12-07
- 使用从字符串中提取的参数调用函数 2022-01-01
- 如何“在 Finder 中显示"或“在资源管理器中显 2021-01-01
- 为什么语句不能出现在命名空间范围内? 2021-01-01
- 如何在不破坏 vtbl 的情况下做相当于 memset(this, ...) 的操作? 2022-01-01
- 在 C++ 中循环遍历所有 Lua 全局变量 2021-01-01
- 从 std::cin 读取密码 2021-01-01
- Windows Media Foundation 录制音频 2021-01-01
- 为 C/C++ 中的项目的 makefile 生成依赖项 2022-01-01