How to have multiple objects of a class with an std::mutex? - c++

I have the following error:
filesystem.hpp:11:7: error: use of deleted function ‘std::mutex& std::mutex::operator=(const std::mutex&)’
In file included from /usr/include/c++/6.1.1/mutex:44:0,
from includes.hpp:11,
from htmlparser.hpp:4,
from htmlparser.cpp:1:
/usr/include/c++/6.1.1/bits/std_mutex.h:98:12: note: declared here
mutex& operator=(const mutex&) = delete;
^~~~~~~~
...about which there are already some questions asked (like this one and that one). Based on these I tried the following class code:
class Filesystem {
private:
std::string dir = getCurrentPath();
mutable std::mutex fsMut;
public:
Filesystem() {}
~Filesystem() {}
Filesystem(const Filesystem&) : fsMut() { }
//Few more functions
};
...which sadly enough doesn't work (or even change the errors).
Now how does my code differ from the previously mentioned questions: In two classes I have in the private section the declaration Filesystem fs;. Which in my opinion would be totally fine, however, while for one class it passes for the other class this error is returned (along with the errors how that class and Filesystem are deleted implicitly).
So I am not copying or moving the class afaik, but what does go wrong then? And how can I alter my code to make it work?
EDIT:
Full error:
htmlparser.cpp: In member function ‘strSet Htmlparser::parseLinks(std::__cxx11::string, std::__cxx11::string, std::__cxx11::string)’:
htmlparser.cpp:10:39: error: use of deleted function ‘Robotsparser& Robotsparser::operator=(const Robotsparser&)’
rsmap[origUrl] = Robotsparser(origUrl);
^
In file included from htmlparser.hpp:5:0,
from htmlparser.cpp:1:
robotsparser.hpp:7:7: note: ‘Robotsparser& Robotsparser::operator=(const Robotsparser&)’ is implicitly deleted because the default definition would be ill-formed:
class Robotsparser {
^~~~~~~~~~~~
robotsparser.hpp:7:7: error: use of deleted function ‘Filesystem& Filesystem::operator=(const Filesystem&)’
In file included from robotsparser.hpp:5:0,
from htmlparser.hpp:5,
from htmlparser.cpp:1:
filesystem.hpp:11:7: note: ‘Filesystem& Filesystem::operator=(const Filesystem&)’ is implicitly deleted because the default definition would be ill-formed:
class Filesystem {
^~~~~~~~~~
filesystem.hpp:11:7: error: use of deleted function ‘std::mutex& std::mutex::operator=(const std::mutex&)’
In file included from /usr/include/c++/6.1.1/mutex:44:0,
from includes.hpp:11,
from htmlparser.hpp:4,
from htmlparser.cpp:1:
/usr/include/c++/6.1.1/bits/std_mutex.h:98:12: note: declared here
mutex& operator=(const mutex&) = delete;
^~~~~~~~
And the other classes:
class Robotsparser {
private:
std::string url;
Filesystem fs;
public:
Robotsparser(std::string u) : url(u) {}
~Robotsparser() {}
};
class A {
private:
std::mutex crawlMut;
Filesystem fs;
public:
A(std::string);
};
Class A is compiled earlier in the Makefile, which might explain why it gives the error at class Robotsparser.

Your error means that there is an assignment operation trying to happen somewhere...
In your Filesystem class, the compiler didn't complain of the copy constructor:
Filesystem(const Filesystem&) : fsMut() {} //because there is no copying of fsMut here
But, the compiler generates a copy assignment operator for you since you didn't define one. And in the compiler generated one, it calls the copy assignment operator of each member.
For what I think your intent are: You should define all your Copy/Move Assignment operators (and Constructors), and make sure you do not try to copy/move the mutex owned by any of the instances.
Filesystem(const Filesystem& f2)
{
std::lock_guard<std::mutex> lk2(f2.fsMut);
/*do your stuff but do not copy f2.fsMut*/
}
Filesystem(Filesystem&& f2)
{
std::lock_guard<std::mutex> lk2(f2.fsMut);
/*do your stuff but do not move f2.fsMut*/
}
Filesystem& operator = (const Filesystem&)
{
std::lock(this->fsMut, f2.fsMut);
std::lock_guard<std::mutex> lk1(this->fsMut, std::adopt_lock);
std::lock_guard<std::mutex> lk2(f2.fsMut, std::adopt_lock);
//do your stuff but do not copy fsMut
return *this;
}
Filesystem& operator = (Filesystem&& f2)
{
std::lock(this->fsMut, f2.fsMut);
std::lock_guard<std::mutex> lk1(this->fsMut, std::adopt_lock);
std::lock_guard<std::mutex> lk2(f2.fsMut, std::adopt_lock);
//do your stuff but do not move fsMut
return *this;
}
Full illustration here: http://coliru.stacked-crooked.com/a/75d03fd564f8b570
Also, consider using, lock_guard's on both mutexes and std::lock to lock both mutexes in the copy/move assignment operators.
Though I still have my reservations on your intent, I saw a member declaration like:
mutable std::mutex fsMut;
The use of mutable is to modify members from a const member function; here its typically for being able to lock/unlock the mutex from a const member function.

What does the first bit of the error say?
htmlparser.cpp: In member function ‘strSet Htmlparser::parseLinks(std::__cxx11::string, std::__cxx11::string, std::__cxx11::string)’:
htmlparser.cpp:10:39: error: use of deleted function ‘Robotsparser& Robotsparser::operator=(const Robotsparser&)’
rsmap[origUrl] = Robotsparser(origUrl);
It says that in Htmlparser::parseLinks, on this line
rsmap[origUrl] = Robotsparser(origUrl);
you're using a copy assignment operator that doesn't exist.
The reason it doesn't exist is that the compiler didn't generate it for you, because it can't, because the mutex member of your class isn't copyable.
However, your real question is why the compiler is trying to use this in the first place:
So I am not copying or moving the class afaik
but you can see, on the line the compiler quoted, an assignment. You haven't shown what rsmap is, but looking at operator[] for std::map shows that it default constructs an element, returns a reference to the new value, and then your code copy-assigns to it. The same is true for std::unordered_map.
If your class isn't copyable or assignable, you can't do this - you need to construct the object in place. The emplace method does this, giving code something like:
rsmap.emplace(origUrl, origUrl);
Alternatively, you can keep your existing code and write the copy/move constructors and assignment operators.

Related

How do I return a non-movable (but copyable) object?

Edit: End goal: I want to make a container class that never uses move, even when it's available. NonMove is a class of test objects for that container.
I tried different variations, but GCC insists on wanting to use move.
class NonMove {
public:
NonMove() {}
// Copy.
NonMove(const NonMove&) {}
NonMove& operator=(const NonMove&) {}
// Move
NonMove(NonMove&&) = delete;
NonMove& operator=(NonMove&&) = delete;
};
NonMove foo() {
return NonMove();
}
Error with GCC 4.9.1 with -std=gnu++11
move.cc: In function ‘NonMove foo()’:
move.cc:15:18: error: use of deleted function ‘NonMove::NonMove(NonMove&&)’
return NonMove();
^
move.cc:10:3: note: declared here
NonMove(NonMove&&) = delete;
^
End goal: I want to make a container class that never uses [move], even when it's available. NonMove is a class of test objects for that container.
Your class does not necessarily help achieve your goal, because deleting the move constructor means the type is not CopyConstructible, so cannot be used to accurately test a container that only copies.
To prevent moving you can ensure that all potential copies or moves are done with a const source object:
NonMove foo() {
return const_cast<const NonMove&&>(NonMove());
}
The move constructor isn't viable if the RHS is const.
It's usually sufficient to just ensure the RHS is an lvalue, as that won't be moved either, but there is a special case for function return values, which can be moved even if they are lvalues, meaning this would try to use the move constructor if it exists:
NonMove foo() {
NonMove nm;
return nm;
}
Adding const ensures it will be copied instead:
NonMove foo() {
NonMove nm;
return const_cast<const NonMove&>(nm);
}

implicit vs. explicit deleted copy constructor

I've recently experienced some strange behaviour in my C++11 code.
I've got a class, which is only movable:
class only_move
{
public:
only_move() : value(0) {}
only_move(int value) : value(value) {}
only_move(const only_move&) = delete;
only_move& operator=(const only_move&) = delete;
only_move(only_move&&) = default;
only_move& operator=(only_move&&) = default;
int value;
};
And I've got another class, which contains an only_move object:
class has_only_move_member
{
public:
has_only_move_member() = delete;
has_only_move_member(only_move&& value) : member(std::move(value)) {}
only_move member;
};
If I understood it correct, that means has_only_move_member can't be copied, as the only_move member can't be copied. This means has_only_move_member(const has_only_move_member&) is deleted implicitly. Let's check that out:
has_only_move_member object(only_move(5));
has_only_move_member copy_of_object(object);
As expected, it prints out:
error: use of deleted function ‘has_only_move_member::has_only_move_member(const has_only_move_member&)’
note: ‘has_only_move_member::has_only_move_member(const has_only_move_member&)’ is implicitly deleted because the default definition would be ill-formed:
class has_only_move_member
Okay, so I moved on and put has_only_move_member instances in a std::map. As they don't have a copy constructor, I've moved them into it:
has_only_move_member object(only_move(5));
std::map<int, has_only_move_member> data;
data.emplace(5, std::move(object));
So far so good. This works like a charm.
But I had an idea. What about being more explicit and explicitly deleting the copy constructor of has_only_move_member. So I wrote into the class has_only_move_member:
has_only_move_member(const has_only_move_member&) = delete;
After that, the same code from above, there I moved objects into an map gives me an error:
/usr/include/c++/4.8/bits/stl_pair.h:134:45: error: use of deleted function ‘has_only_move_member::has_only_move_member(const has_only_move_member&)’
Yeah of course it's deleted, but why is there such a gap between implicit and explicit delete?
I'm getting the same behaviour with g++ 4.8.2 and clang 3.4-rc1 on Debian with libstdc++ and recent apple-clang-llvm 4.0 on Mac OS X 10.9 with libc++
I already said this in the comment but since this is a correct answer I am reposting as one:
You added a custom constructor (even though it's a deleted one). Therefore the compiler won't auto generate a move constructor. The compiler then falls back to copying which (as you requested) it obviously can't do.

'Type non-static const member can't use default assignment operator' - what does this mean?

This class is flagging up the following error:
'non-static const member 'const int Member::membershipNo', can't use default assignment operator'. The strange thing is this code is repeated in another project and works perfectly. Can you help me put it right?
Member.h
class Member : public Person
{
public:
Member();
Member(int membershipNo);
virtual ~Member();
int getMembershipNo() const;
private:
const int membershipNo;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & boost::serialization::base_object<Person>(*this);
ar & membershipNo;
}
};
Member.cpp
Member::Member() : membershipNo(0)
{
Person();
}
Member::Member(int memberNo) : membershipNo(memberNo)
{
Person();
}
Member::~Member()
{
// TODO Auto-generated destructor stub
}
int Member::getMembershipNo() const
{
return membershipNo;
}
Presumably, somewhere in your code you are assigning to a Member, something like this:
Member m1, m2;
m1 = m2;
Or you are using it in a context that requires the type to be assignable.
Since you don't provide your own assignment operator overload for Member, the implicitly defined defaulted assignment operator would usually kick in. However, since you have a const data member, the compiler won't implicitly define one for you. You need to provide it yourself.
This makes sense because, imagine in the code sample I just gave, what should the compiler do to m1's membershipNo member? Should it assign m2's membershipNo to it? How can it do that if membershipNo is const? In this case, the compiler just says "Nope, I can't do it."
A defaulted copy/move assignment operator for class X is defined as deleted if X has:
a non-static data member of const non-class type (or array thereof)
[...]
As I said, to be able to do assignment with a Member, you need to provide an assignment operator of your own:
Member& Member::operator=(const Member& other)
{
// Copy things from other to this
}
However, herein lies the problem with having const members. If you supply your own copy assignment operator as described, and you don't copy over membershipNo, then have you really copied the other object? Logically, it makes sense that an object that has any const state shouldn't be able to be assigned to.
It is perfectly fine to use a copy constructor however - you only have to make sure you initialise membershipNo in the member initialization list:
Member::Member(const Member& other)
: membershipNo(other.membershipNo)
{
// ...
}
And then you can do:
Member m1;
Member m2 = m1;
What is happening is that some client code is attempting to use assign one Member instance to another one. Since you have a constant data member, this cannot work. The error will only come up if some code attempts to make the assignment, which is why it may seem the same class "works" in another project.
In terms of putting it right, the options are A) not to perform the assignment, or B) make the data member non-const.

std::vector::push_back a non-copyable object gives compiler error

I get compilation errors on g++ (GCC) 4.7.2 but not on MSVC-2012 when trying to std::vector::push_back a non-copyable (private copy constructor) but moveable object. To me my example looks identical to many other examples on SO and elsewhere. The error message makes it looks like a problem with the struct not being 'direct constructible' - I don't know what this means so am doubly unsure about why an object needs to be 'direct constructible' to be pushed back.
#include <vector>
#include <memory>
struct MyStruct
{
MyStruct(std::unique_ptr<int> p);
MyStruct(MyStruct&& other);
MyStruct& operator=(MyStruct&& other);
std::unique_ptr<int> mP;
private:
// Non-copyable
MyStruct(const MyStruct&);
MyStruct& operator=(const MyStruct& other);
};
int main()
{
MyStruct s(std::unique_ptr<int>(new int(5)));
std::vector<MyStruct> v;
auto other = std::move(s); // Test it is moveable
v.push_back(std::move(other)); // Fails to compile
return 0;
}
Gives errors
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/type_traits: In instantiation of ‘struct std::__is_direct_constructible_impl<MyStruct, const MyStruct&>’:
... snip ...
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_vector.h:900:9: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = MyStruct; _Alloc = std::allocator<MyStruct>; std::vector<_Tp, _Alloc>::value_type = MyStruct]’
main.cpp:27:33: required from here
main.cpp:16:5: error: ‘MyStruct::MyStruct(const MyStruct&)’ is private
Simple workaround from various answers:
Use MyStruct(const MyStruct&) = delete; instead of private ctor hack
Inherit boost::noncopyable (or another class with private ctor)
The failure is due to a limitation of G++ 4.7, which doesn't implement DR 1170, which was changed very late in the C++11 standardisation process to say that access checking should be done as part of template argument deduction.
The underlying cause is that libstdc++'s vector will move elements if the move operation is guaranteed not to throw (i.e. it's declared noexcept or throw()), otherwise if the type is copyable the elements will be copied, otherwise if the type is not copyable but does have a possibly-throwing move operation then it will be moved (and if an exception is thrown the results are undefined unspecified.) This is implemented with checks to the is_nothrow_move_constructible and is_copy_constructible type traits. In your case, the type is not nothrow move constructible, so the is_copy_constructible trait is checked. Your type has a copy constructor but it's not accessible, so the is_copy_constructible trait produces a compiler error with G++ 4.7 because access checking is not done during template argument deduction.
If you make your move constructor and move assignment operator noexcept then the type will be moved and doesn't need to be copyable, so the is_copy_constructible trait that fails is not used, and the code compiles OK.
Alternatively, (as also stated in the comments) if you make the copy constructor deleted then the is_copy_constructible trait gets the right result.
Another alternative is to use something like boost::noncopyable which implicitly makes the copy constructor deleted so the is_copy_constructible trait works properly (and also works with older compilers like MSVC that don't support deleted functions properly). I don't know what you mean about making it impossible to find the error, does MSVC not show you the full context of a compiler error?
Conclusion: use unique_ptr where appropriate but don't make classes explicitly movable
I disagree with this conclusion, it is too extreme. Instead make your classes nothrow movable whenever possible. Also, when possible, use deleted functions to make a type non-copyable instead of private+unimplemented functions, maybe using a macro for portability to older compilers e.g.
#if __cplusplus >= 201103L
#define NONCOPYABLE(TYPE) \
TYPE(const TYPE&) = delete; TYPE& operator=(const TYPE&) = delete
#else
// must be used in private access region
#define NONCOPYABLE(TYPE) \
TYPE(const TYPE&); TYPE& operator=(const TYPE&)
#endif
struct MyStruct
{
...
private:
NONCOPYABLE(MyStruct);
};

Const correctness with temporary instances

Here is the example of the "scoped lock" idiom with common mistake: no local variable is created, so lock is not in effect. This code compiles flawlessly both with VC++ 2010 and Comeau C++ online:
class Mutex
{
public:
void lock() {}
};
class ScopedLock
{
public:
ScopedLock() : m_pm(0) {}
ScopedLock(Mutex& m) : m_pm(&m) { m_pm->lock(); }
private:
Mutex* m_pm;
private:
ScopedLock& operator =(const ScopedLock&);
ScopedLock(const ScopedLock&);
};
class X
{
public:
void foo() const
{
ScopedLock(m_mutex);
}
private:
Mutex m_mutex;
};
int main()
{
X x1;
x1.foo();
}
If default constructor for ScopedLock is commented out, then both compilers give an error:
error C2512: 'ScopedLock' : no appropriate default constructor available
(When ScopedLock used correctly, i.e. local variable is created: ScopedLock guard(m_mutex);, then compilation fails as expected. Declaring m_mutex as mutable fixes the problem.)
I have two questions:
Why X::foo compiles? It seems that compiler was able to cast const Mutex& to Mutex& somehow.
What role plays ScopedLock default constructor, so the compilation succeeds?
Thanks.
Update: I found the answer. It appears that ScopedLock(m_mutex); statement creates a local variable m_mutex of type ScopedLock. Not a temporary. That's why ScopedLock::ScopedLock default constructor is required.
You answered the question yourself.
It appears that ScopedLock(m_mutex); statement creates a local variable m_mutex of type ScopedLock
The explanation is to be found in the Standard's Section 6.8 Ambiguity Resolution:
There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion [5.2.3] as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
The Standard then lists T(a); as an example of a statement that is really a declaration. It is equivalent to T a;
This is one variation of the infamous C++ "most vexing parse".
I'm pretty sure your problem is line 26: ScopedLock(m_mutex);
Instead, that should be something like ScopedLock a_variable_name(m_mutex);
When I make that change, I get the expected errors:
constCorrectness.cpp: In member function ‘void X::foo() const’:
constCorrectness.cpp:26: error: no matching function for call to ‘ScopedLock::ScopedLock(const Mutex&)’
constCorrectness.cpp:18: note: candidates are: ScopedLock::ScopedLock(const ScopedLock&)
constCorrectness.cpp:11: note: ScopedLock::ScopedLock(Mutex&)
constCorrectness.cpp:10: note: ScopedLock::ScopedLock()
Perhaps somebody can interpret ScopedLock(m_mutex) for us? Does it declare a function or something? Instead of calling a constructor as expected by the questioner? Update: Striking this out. I think it's just a variable declaration (i.e. the brackets are ignored.)
The problem is that the X::foo() method is declared as const - that means that it will not mutate (change) the object.
The ScopedLock() constructor doesn't have a overload that accepts a immutable (const) reference to a Mutex object.
Fixing this requires you declare m_mutex as mutable, or provide an appropriate overloaded ScopedLock() constructor. I'm thinking the former is preferable.