使用临时实例的const正确性

时间:2011-05-19 01:34:12

标签: c++

以下是具有常见错误的“范围锁定”习惯用法的示例:未创建局部变量,因此锁定无效。这段代码在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时,即创建了局部变量:ScopedLock guard(m_mutex);,然后编译按预期失败。将m_mutex声明为可变修复问题。)

我有两个问题:

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

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

  3. 感谢。

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

3 个答案:

答案 0 :(得分:4)

你自己回答了这个问题。

  

看来是ScopedLock(m_mutex); statement创建一个ScopedLock类型的局部变量m_mutex

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

  

语法中涉及表达式语句和声明存在歧义:具有函数式显式类型转换[5.2.3]的表达式语句,因为其最左侧的子表达式与第一个声明符以第一个声明符开头的声明无法区分a(。在这些情况下,声明是声明

标准然后将T(a);列为实际声明的语句示例。它相当于T a;

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

答案 1 :(得分:1)

我很确定你的问题是第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)?它是否声明了某个功能?而不是像提问者那样调用构造函数? 更新:明白这一点。我认为这只是一个变量声明(即括号被忽略。)

答案 2 :(得分:0)

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

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

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