Search code examples
c++raiimost-vexing-parse

Forcing non-temporary in RAII by compile Error C++


When one uses lock_guard in C++ like this:

 lock_guard<mutex>(lock);

The compiler complains:

no matching constructor for initialization of 'std::lock_guard<std::mutex>'

Because the correct use is:

 lock_guard<mutex> guard(lock);

I would like to write a custom RAII allocator that also coplains when this happens - I tried disabling copy consturctor and assigment operator, but so far nothing works.


Solution

  • I don't know how to achieve your goal, but I guess I know what's happened to lock_guard<mutex>(lock).

    Let's do some experiment.

    Experiment 1

    int(x);
    x = 1;
    std::cout << x << std::endl;
    

    The experiment reveals we have declared a variable x, even though there is a pair of brackets.

    Experiment 2

    class Widget
    {
    };
    
    class WidgetGuard
    {
    private:
        Widget& widget;
    public:
        WidgetGuard(Widget& w)
            : widget(w)
        {}
    };
    
    int main()
    {
        Widget w1;
    
        WidgetGuard wg1(w1);
        WidgetGuard(w1);     //Error!
        WidgetGuard{ w1 };   //Pass!
    }
    

    We define a Widget class and WidgetGuard class to emulate std::mutex and std::lock_guard. When we try to declare a temporary WidgetGuard with brackets, it gives an error. But doing so with braces compiles.

    This can be explained with Experiment 1. The compiler parses WidgetGuard{ w1 } to be "create temporary". But it parses WidgetGuard(w1) to be "declare variable w1"! There are two sources of error: reusing the same name, and no default constructor in WidgetGuard.

    Experiment 0

    Returning to std::mutex and std::lock_guard, let's try braces...

    std::mutex m;
    std::lock_guard<std::mutex> {m};
    

    ... and it works.

    A temporary of std::lock_guard can be created. We just can.