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_ptrs. 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>();
}
Related
I've got something along the lines of:
#include <utility>
#include <cstdlib>
struct Core
{
void* mData{};
size_t mCount{};
};
template <typename T>
struct Actual: protected Core
{
Actual() = default;
Actual(Actual<T> const& other) = delete;
Actual<T>& operator=(Actual<T> const& other) = delete;
Actual(Actual<T>&& other):
Core{ other.mData, other.mCount }
{
other.mData = nullptr;
other.mCount = 0;
}
Actual<T>& operator=(Actual<T>&& other)
{
Actual<T> tmp(std::move(other));
std::swap(mData, tmp.mData); // with -O2 -Wall -Wextra -Werror: "error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]"
mCount = tmp.mCount; // ditto
return *this;
}
~Actual() { /* delete[] mData as array of T, if it was created */ }
// rest of the API incl. allocating that array of T into mData, not relevant
};
int main() {
Actual<int> a;
Actual<int> b;
b = std::move(a);
}
This compiles fine until optimizations are requested (-O2 and above).
The question is simply, why does the compiler (GCC 9.3.0) think that this is type punning / a violation of strict aliasing rule, and what can I do to fix this?
Looks like a GCC bug. GCC 9.4 and newer don't diagnose this.
If you rewrite operator= like this, it stops complaining, and this form is also shorter.
Actual<T>& operator=(Actual<T> other)
{
std::swap(mData, other.mData);
std::swap(mCount, other.mCount); // I like this more than `mCount = tmp.mCount;`.
return *this;
}
For it to work, you have to remove Actual<T>& operator=(Actual<T> const& other) = delete;, and you might as well remove Actual(Actual<T> const& other) = delete;.
You also should add noexcept to the move constructor and to this assignment operator, lest standard containers (when resized) decide to copy instances of your class instead of moving them.
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_ptrs. 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>();
}
I have an object that I want to restrict to be allocated only inside of a std::map. Here is the simplified code:
#include <map>
class Value
{
public:
Value(int value) { _value = value;}
Value(const Value&) = delete;
Value& operator=(const Value&) = delete;
Value(Value&&) = default; // ***
void* operator new(size_t) = delete; // not on free store
private:
int _value;
};
class Container
{
public:
Container();
Value* addToMap(int key) {
auto ret = _map.emplace(key, key);
return &(ret.first->second);
}
private:
std::map<int, Value> _map;
};
In order to make it compile on Mac using CLang I had to add a line marked by asterisks requesting default move constructor. However this line causes C2610 error when compiled in Windows Visual Studio. Looks like VS2013 C++11 non-compliance includes inability to generate default move constructors. Is there a different way for me to allocate an object inside of standard map that would compile across platforms or do I have to implement my own move constructor?
An option is to use std::piecewise_construct:
Value* addToMap(int key) {
auto ret = _map.emplace(std::piecewise_construct
, std::forward_as_tuple(key)
, std::forward_as_tuple(key));
return &(ret.first->second);
}
VC++ DEMO
I was implementing (for training purpose) a Bubble Sort template function:
template<typename iterInput,
typename predicate>
void BubbleSort(iterInput first1,iterInput last1,predicate func)
{
bool swapped(false);
do
{
swapped = false;
iterInput begin = first1;
iterInput beginMinus = first1;
++begin;
for (;begin != last1; begin++,beginMinus++)
{
if (func(*beginMinus,*begin) )
{
std::swap(*beginMinus,*begin);
swapped = true;
}
}
}
while(swapped);
}
When I have realized that this function will not work for class with no assignment operator, like this one (forgive me for the bad name):
class NoCopyable
{
public:
explicit NoCopyable(int value) : value_(value) {}
NoCopyable(const NoCopyable& other) : value_(other.value_) {}
~NoCopyable() {}
bool operator<(const NoCopyable& other) { return value_ < other.value_; }
void setValue(int value) { value_ = value; }
std::ostream& print(std::ostream& os) const { return os << value_; }
private:
NoCopyable& operator=(const NoCopyable& other);
int value_;
};
std::ostream& operator<<(std::ostream& os, const NoCopyable& obj)
{
return obj.print(os);
}
struct PrintNoCopyable
{
void operator()(const NoCopyable& noCopyable) { std::cout << noCopyable << '\n'; }
};
The compiler raises this error Error 1 error C2248: 'NoCopyable::operator =' : cannot access private member declared in class 'NoCopyable'
So, I have slightly modify the code using instead of the std::swap function my version of the swap function, here is the code:
template<typename T1,
typename T2>
void noAssignmentSwap(T1& t1,T2& t2)
{
T1 temp(t1);
t1.~T1();
new (&t1) T1(t2);
t2.~T2();
new (&t2) T2(temp);
}
The code compiles and gives the right result. However I am not completely sure, I remember a Sutter's article that suggest you to avoid playing with the objects life time. The article just warns you by playing with fire without actually giving you any real reason. I can see problem in exception safety if the copy constructor of T1 or T2 can throw. However there is the same problem in the standard version if the assignment operator is allowed to throw.
Here the question, can you see any possible drawbacks in this version of swap?
Cheers
Apart from anything else, if a class does not have an assignment operator, its designer probably did not intend it to be swapped. If they did that, they probably disabled copy construction too, so your new swap function still won't work.
As for your assertion that Standard Library containers do not need assignment - that is true so long as you don't want to actually do anything useful with them. Does this code compile for you?
#include <vector>
using namespace std;
struct A {
private:
void operator=( const A &);
};
int main() {
vector <A> v;
v.push_back( A() );
v[0] = A(); // assignment needed here
}
I think it won't.
The difference is that when the assignment operator fails, you still have the same number of objects.
If you destroy one object and fail to create a new one, one object is lost! If it was part of a container, the container's state is probably also invalid.
You need a copy ctor instead of an assignment operator, but the two are sufficiently similar that at least in a typical case, you'll have both or you'll have neither. IOW, I don't think this generally accomplishes much.
I'd class it right along side the xor-swap trick: interesting, but generally useless.
It might be confusing to a future maintainer of the code.
Working on a toy project that I started to answer an SO question I'm getting flooded by a g++ warning that I don't understand.
format.hpp:230: warning: dereferencing pointer ‘<anonymous>’
does break strict-aliasing rules
searching on the internet I've got the impression that this could be a g++ bug; is it really a bug and if yes is there any workaround for it? The full source code is too big for inclusion but is available here. Here is the part where the warning is triggered...
template<typename T>
class ValueWrapper : public ValueWrapperBase
{
public:
T x;
ValueWrapper(const T& x) : x(x) {}
virtual std::string toString(const Field& field) const
{
return Formatter<T>().toString(x, field);
}
private:
// Taboo
ValueWrapper(const ValueWrapper&);
ValueWrapper& operator=(const ValueWrapper&);
};
typedef std::map<std::string, ValueWrapperBase *> Env;
class Dict
{
private:
Env env;
public:
Dict() {}
virtual ~Dict()
{
for (Env::iterator i=env.begin(), e=env.end(); i!=e; ++i)
delete i->second;
}
template<typename T>
Dict& operator()(const std::string& name, const T& value)
{
Env::iterator p = env.find(name);
if (p == env.end())
{
env[name] = new ValueWrapper<T>(value);
}
else
{
ValueWrapperBase *vw = new ValueWrapper<T>(value);
delete p->second;
p->second = vw;
}
return *this;
}
const ValueWrapperBase& operator[](const std::string& name) const
{
Env::const_iterator p = env.find(name);
if (p == env.end())
throw std::runtime_error("Field not present");
return *(p->second);
}
private:
// Taboo
Dict(const Dict&);
Dict& operator=(const Dict&);
};
Line 230 is p->second = vw;.
I get the warning for every instantiation of the template method operator(), always about line 230.
EDIT
Apparently the bug is about the use of map iterators that can generate inline code that confuses the optimizer. Rewriting a section avoiding using iterators I got shorter code that also compiles cleanly without warnings.
template<typename T>
Dict& operator()(const std::string& name, const T& value)
{
ValueWrapperBase *vw = new ValueWrapper<T>(value);
ValueWrapperBase *& p(env[name]);
delete p;
p = vw;
return *this;
}
As far as I can tell this actually stems from code in map and not from your code itself.
According to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42032 and http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43978 which both deal with maps and are very similar to each other, that there are absolutely some cases where it warns incorrectly because it loses track of the dynamic types of the objects. They equally state that there are some cases where it warms properly.
Also they indicate that the warning is shushed in 4.5 until they can implement it properly.
Finally, did you try rewriting your method as follows to see if it helps the warning in 4.3/4.4?
template<typename T>
Dict& operator()(const std::string& name, const T& value)
{
ValueWrapperBase *vw = new ValueWrapper<T>(value);
delete env[name];
env[name] = new ValueWrapper<T>(value);
return *this;
}
-fno-strict-aliasing (see http://gcc.gnu.org/onlinedocs/gcc-4.2.4/gcc/Optimize-Options.html#index-fstrict_002daliasing-572) turns off gcc's strict aliasing optimisations, and (presumably) with it the warning.
See also What is the strict aliasing rule? and http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html.
I've seen this "error" before and decided that it's often meaningless. I don't see anything wrong with your code. You might try your luck with newer versions of GCC--I seem to recall seeing this pop up somewhere around 4.3-4.4.
Edit: I said this warning/error is "often" meaningless. Not "usually." I absolutely do not advocate simply ignoring or disabling warnings just because they are annoying, but in this code, and in some of my own code, there is no apparent problem despite GCC's complaint.