Reputation: 3985
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.
Upvotes: 6
Views: 318
Reputation: 1601
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".
Upvotes: 4
Reputation: 3708
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.
Upvotes: 0
Reputation: 27123
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 Update: Striking this out. I think it's just a variable declaration (i.e. the brackets are ignored.) ScopedLock(m_mutex)
for us? Does it declare a function or something? Instead of calling a constructor as expected by the questioner?
Upvotes: 1