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();
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.
Update:
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>&&>())), "");
Your compiler doesn't think std::unordered_map<MoveOnly, int, Hasher>
can be noexcept
move-constructed.
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.
Copying is obviously not possible.
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.
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.
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; }
};
Here is my test harness:
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>();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With