临时实例的常量正确性

Const correctness with temporary instances

本文关键字:常量 正确性 实例      更新时间:2023-10-16

这是"作用域锁"习惯用法的示例,其中包含常见错误:没有创建局部变量,因此锁无效。这段代码在VC++ 2010和Comeau C++在线完美编译:

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();
}

如果 ScopedLock 的默认构造函数被注释掉,则两个编译器都会给出错误:

错误 C2512:"作用域锁定":没有可用的适当默认构造函数

(当正确使用ScopedLock时,即创建局部变量:ScopedLock guard(m_mutex);,则编译按预期失败。将m_mutex声明为可变可解决此问题。

我有两个问题:

  1. 为什么要X::foo编译?编译器似乎能够以某种方式将const Mutex&转换为Mutex&

  2. 默认构造函数ScopedLock什么角色,因此编译成功?

谢谢。

更新:我找到了答案。似乎ScopedLock(m_mutex);语句创建了一个类型为 ScopedLock 的局部变量m_mutex。不是临时的。这就是为什么需要ScopedLock::ScopedLock默认构造函数的原因。

你自己回答了这个问题。

似乎 ScopedLock(m_mutex); 语句创建了一个 ScopedLock 类型的局部变量m_mutex

解释可以在标准的第6.8节歧义解决方案中找到:

涉及表达式语句和声明的语法中存在歧义:将函数样式显式类型转换 [5.2.3] 作为其最左侧子表达式的表达式语句与第一个声明符以 (.在这些情况下,声明是声明

然后,标准将T(a);列为真正声明的声明的示例。它相当于T a;

这是臭名昭著的C++"最令人烦恼的解析"的一种变体。

我很

确定你的问题是第 26 行:ScopedLock(m_mutex);

相反,这应该是类似ScopedLock a_variable_name(m_mutex);

当我进行该更改时,我收到预期的错误:

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()

也许有人可以为我们解释ScopedLock(m_mutex)?它是否声明了一个函数或其他东西?而不是像提问者期望的那样调用构造函数?更新:删除这个。我认为这只是一个变量声明(即括号被忽略。

问题是 X::foo() 方法被声明为 const - 这意味着它不会改变(更改)对象。

构造函数没有接受对互斥对象的不可变(const)引用的重载。

解决此问题需要将m_mutex声明为可变的,或者提供适当的重载 ScopedLock() 构造函数。 我认为前者更可取。