我正在将我的项目从VS2015移动到VS2017,当然这并不顺利。
我看到奇怪的编译器错误,可以通过以下代码重现:
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编译器。
更新
在Yakk发现问题后,我尝试使用其他容器代替unordered_map
,代码只编译vector
。
答案 0 :(得分:5)
所以问题似乎是这样的:
static_assert( noexcept(std::unordered_map<MoveOnly, int, Hasher>( std::declval<std::unordered_map<MoveOnly, int, Hasher>&&>())), "");
您的编译器认为std::unordered_map<MoveOnly, int, Hasher>
不能noexcept
移动构建。
然后,MSVC 2017附带的偏执std::vector
实现依赖于复制元素,以便在向量调整大小时生成强大的异常保证。
显然无法复制。
现在std::unordered_map<int, int>
也是如此 - MSVC认为移动它也冒着抛出异常的风险;我相信你无法用密钥或散列类型做任何事情都可能使unordered_map
异常安全的移动构造函数。
没有充分的理由让unordered_map(unordered_map&&)
不成为noexcept
。我不确定标准是允许它还是强制它,或者它是否是编译器中的错误;如果标准要求它为noexcept(false)
,那么标准就有缺陷。
您可以通过存储unique_ptr
s的向量来解决此问题。或者编写一个包含异常的value_ptr
,但代码更改次数更少:
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>();
}