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.
Related
I'm trying to write a class include a public thread object, and finally I want to access this thread ouside the class, but there are some errors, why?
The class defined as:
class scoped_thread {
public:
std::thread t;
explicit scoped_thread(std::thread t_) : t(std::move(t_)) {
if (!t.joinable())
throw std::logic_error("No thread");
}
~scoped_thread() {
// t.join();
}
scoped_thread(scoped_thread const &) = delete;
scoped_thread & operator = (scoped_thread const &) = delete;
};
and I'm try to access thread member:
scoped_thread th(std::thread(f));
th.t.join();
At last, the error is:
error: request for member 't' in 'th', which is of non-class type 'scoped_thread(std::thread)'
scoped_thread th(std::thread(f)); is a declaration of a function named th, returning scoped_thread and taking std::thread as a parameter. It's not a declaration of an object of type scoped_thread. Naturally, the function doesn't have members, named t or otherwise.
See also: most vexing parse.
You've run into the most vexing parse. C++ thinks you've declared a function named th instead of an object. In recent versions of C++, to correct the issue you can use curly braces instead of parentheses.
scoped_thread th{std::thread{f}}; // compiles as expected
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.
I have the following piece of code.
class A {
public:
A(int) {
}
};
int a;
int main() {
A(a); // Line 'a
return 0;
}
What I want to do on line 'a is to create a temporary A with constructor A::A(int). I know it's going to destruct immediately. That's what I want. But it seems the compiler is doing something equivalent to A a, defining variable a as of class A and initialize it using constructor A::A(). Of course it doesn't exist, hence the compiler error.
However, if I change my code to the following.
class A {
public:
A(int) {
}
};
void f(A) {
}
int a;
int main() {
f(A(a));
return 0;
}
It works fine now. The compiler constructs a temporary A and use it to call f.
Why is A(a) different in both contexts? How is it stated in the standard or for some obscure reason? How can I construct a temporary object as in the first code sample?
This is another instance of the "everything that can be a declaration is a declaration" rule. [stmt.ambig]/p1:
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 provides the following examples:
Assuming T is a simple-type-specifier,
T(a)->m = 7; // expression-statement
T(a)++; // expression-statement
T(a,5)<<c; // expression-statement
T(*d)(int); // declaration
T(e)[5]; // declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration
In the last example above, g, which is a pointer to T, is initialized
to double(3). This is of course ill-formed for semantic reasons, but
that does not affect the syntactic analysis.
and also:
class T {
// ...
public:
T();
T(int);
T(int, int);
};
T(a); // declaration
T(*b)(); // declaration
T(c)=7; // declaration
T(d),e,f=3; // declaration
extern int h;
T(g)(h,2); // declaration
The simplest way to disambiguate is probably an extra set of parentheses. (A(a)); is unambiguously an expression-statement.
I could not think of a way to create a temporary without using it in some call, like you did with the function.
Why don't you want to create a named variable instead? It's lifecycle will be exactly the same as your "temporary", given how you try to declare it.
If you really want to control the destruction of your object, you could always encapsulate it in a block: { A tmp(a); } // temporary A object
The thing is... I don't see the point of doing that. Why would you want to create a temporary object without using it? It usually means that your object creation or destruction has some side effects that you want to trigger. This is a really bad idea! You should move the side effects to a function and simply call it.
I was digging in somebody else's code where I noticed he assigns the public/private members of a class in the following way:
myMemberVar(Value);
instead of
myMemberVal=Value;
I'm wondering if this way of assigning is normal and can be really used interchangebally.
It's likely that you're looking at initialisations in the member initialisation list.
struct A
{
int x, y;
A()
: x(42) // <-- here
, y(12) // <-- and here
{}
{};
These are not "assignments", any more than the following are assignments:
void foo()
{
int x = 42;
int y = 42;
}
But recall that the C++03 initialisation syntax (as opposed to the above confusing, legacy, looks-like-assignment syntax†) is:
void foo()
{
int x(42);
int y(42);
}
And, going further, since C++11:
void foo()
{
int x{42};
int y{42};
}
And that's the syntax that's required in the member initialisation list.
Note that this means they're not generally interchangeable: only in an initialisation! Usually when you write = you're performing assignment and this initialisation syntax would be invalid there.
† Don't worry, I still prefer it too!
That is the syntax for construction, not assignment.
The first snippet of code doesn't assign anything; it attempts to call myMemberVar like a function.
Perhaps the real code was in the initialiser list of a constructor:
MyClass() : myMemberVar(Value) {}
while the second was in the constructor body:
MyClass() {
myMemberVar = Value;
}
The difference here is that the first performs direct-initialisation, not assignment; the second performs default-initialisation followed by assignment.
For simple types, both have the same effect, and can be regarded as interchangable. For class types, they might call different user-defined functions: a conversion/copy/move constructor in the first case, and a default constructor followed by an assignment operator in the second. Depending on how those functions are defined, the two options may have different effects or performance characteristics, and may not be possible if the required functions aren't accessible.
I'm seeing some different behavior between g++ and msvc around value initializing non-copyable objects. Consider a class that is non-copyable:
class noncopyable_base
{
public:
noncopyable_base() {}
private:
noncopyable_base(const noncopyable_base &);
noncopyable_base &operator=(const noncopyable_base &);
};
class noncopyable : private noncopyable_base
{
public:
noncopyable() : x_(0) {}
noncopyable(int x) : x_(x) {}
private:
int x_;
};
and a template that uses value initialization so that the value will get a known value even when the type is POD:
template <class T>
void doit()
{
T t = T();
...
}
and trying to use those together:
doit<noncopyable>();
This works fine on msvc as of VC++ 9.0 but fails on every version of g++ I tested this with (including version 4.5.0) because the copy constructor is private.
Two questions:
Which behavior is standards compliant?
Any suggestion of how to work around this in gcc (and to be clear, changing that to T t; is not an acceptable solution as this breaks POD types).
P.S. I see the same problem with boost::noncopyable.
The behavior you're seeing in MSVC is an extension, though it's documented as such in a roundabout way on the following page (emphasis mine) http://msdn.microsoft.com/en-us/library/0yw5843c.aspx:
The equal-sign initialization syntax is different from the function-style syntax, even though the generated code is identical in most cases. The difference is that when the equal-sign syntax is used, the compiler has to behave as if the following sequence of events were taking place:
Creating a temporary object of the same type as the object being initialized.
Copying the temporary object to the object.
The constructor must be accessible before the compiler can perform these steps. Even though the compiler can eliminate the temporary creation and copy steps in most cases, an inaccessible copy constructor causes equal-sign initialization to fail (under /Za, /Ze (Disable Language Extensions)).
See Ben Voigt's answer for a workaround which is a simplified version of boost::value_initialized, as pointed out by litb in a comment to Ben's answer. The docs for boost::value_initalized has a great discussion of the problem, the workaround, and some of the pitfalls of various compiler issues.
I don't think template metaprogamming is needed. Try
template <class T>
void doit()
{
struct initer { T t; initer() : t() {} } inited;
T& t = inited.t;
...
}
There's §12.8/14:
A program is ill-formed if the copy constructor or the copy assignment operator for an object is implicitly used and the special member function is not accessible.
And then there's §12.8/15:
When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects.
So, the question is really, if the implementation omits the call to the copy constructor (which it is clearly allowed to do), is the copy constructor actually used?
And, the answer to that is yes, per §3.2/2:
A copy constructor is used even if the call is actually elided by the implementation.
Have you seen what happens when you compile using /Wall with MSVC? It states the following about your class:
nocopy.cc(21) : warning C4625: 'noncopyable' : copy constructor could not be
generated because a base class copy constructor is inaccessible
nocopy.cc(21) : warning C4626: 'noncopyable' : assignment operator could not be
generated because a base class assignment operator is inaccessible
GCC remedy:
create a copy constructor for noncopyable (and an assignment operator ideally!) that does what it can to copy the information from noncopyable_base, namely invoking the constructor for noncopyable_base that has no parameters (since that is the only one accessible by noncopyable) and then copying any data from noncopyable_base. Given the definition of noncopyable_base, however, it seems there is no data to copy, so the simple addition of noncopyable_base() to the initializer list of a new noncopyable(const noncopyable &) function should work.
Take note of what MSVC said about your program though. Also note that if you use T t() rather than T t = T(), another warning (C4930) is generated by MSVC, though GCC happily accepts it either way without any warning issued.