Hi Dragan,

 

the drawback here is that the construction of m is not done within the function lockedMutexFactory().

Perhaps the constructor parameters of std::mutex depend on logic, which cannot be (simply) put into the alias declaration.

So this solution would only work in some of the intended use cases (the simpler ones).

 

Of course one could have to declare the alias and later on would have to explicitly construct it, too.

 -> If it is constructed like an automatic/local variable, it would be unclear that we are not creating a new variable shadowing the old one.

 -> If it is constructed with some markup / new syntax, the usefulness of the alias declaration is even further reduced.

 

Best,

Sebastian
 

-----Ursprüngliche Nachricht-----
Von: Dragan Grbic via Std-Proposals <std-proposals@lists.isocpp.org>
Gesendet: Do 14.09.2023 17:06
Betreff: Re: [std-proposals] Copy-construct, move-construct, and PR-construct
An: std-proposals@lists.isocpp.org;
CC: Dragan Grbic <dgrbic@gmail.com>;
An alternative would be to declare the name (alias) for the return value:
 

auto lockedMutexFactory() -> std::mutex m {

    m.lock();

    return m;               // Either: only m allowed here, or,

    // return std::mutex(); // alternatively, the destructor of m is called first,

                            // and then std::mutex() is returned

}


On Thu, Sep 14, 2023 at 12:33 AM Sebastian Wittmeier via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

Hi Arthur,

so let us focus on the factory function. The value semantics code has to produce the pointer semantics code somehow out of value syntax.

 

I think it is useful to explicitly mark the returned variable at declaration, as it is located in the memory of the caller. Even if the compiler can deduce it from the returned expression.

This is better than marking it (only) at the return IMHO.

 

std::mutex lockedMutexFactory() {

    std::mutex return m; // mark the variable with return keyword;

                         // it is not constructed on the stack,

                         // but at the location provided by the caller

    m.lock();

    return m;               // Either: only m allowed here, or,

    // return std::mutex(); // alternatively, the destructor of m is called first,

                            // and then std::mutex() is returned

}

The variable marked with 'return' should be of the same type as the function return type.

There can be only one variable marked as 'return' (but they can have different names in parallel code blocks, e.g. in the if and the else block).

Each path out of the function (return, throw, ...) either uses the existing marked variable, destructs it and/or creates a new object at that caller-provided location or does nothing.

 

Best,

Sebastian

 

-----Ursprüngliche Nachricht-----
Von: Arthur O‘Dwyer via Std-Proposals <std-proposals@lists.isocpp.org>
Gesendet: Mi 13.09.2023 17:19
Betreff: Re: [std-proposals] Copy-construct, move-construct, and PR-construct
An: std-proposals@lists.isocpp.org;
CC: Arthur O‘Dwyer <arthur.j.odwyer@gmail.com>;
On Tue, Sep 12, 2023 at 2:16 PM Sebastian Wittmeier via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

Should we have unmovable-and-uncopiable return values at all?

 

Even if it can be technically solved for certain cases, those classes are not meant to be moved, copied or returned.

 
s/or returned//
I'd say that such types were always meant to be supported (because why wouldn't they be?), and since C++17 immovable return types have been supported, at least in the cases that Just Work (like `return std::mutex();`). One certainly can't say that std::mutex wasn't "meant" to be returned. It's a class type; of course you can return it from a function! (In spirit, although not literally, each constructor of std::mutex is such a function.)
 

Better change the interface of std::optional::emplace and/or SomeFunctionThatReturnsMutex().

So that

 - SomeFunctionThatReturnsMutex gets a pointer, where to construct its mutex and

 - emplace gets a Callable, which creates the mutex at the right moment in time

 
The problem with that kind of "two-stage initialization" is that it doesn't lend itself to value semantics. You're suggesting that I should write
    std::mutex *lockedMutexFactory(void *where) { auto *p = ::new (where) std::mutex(); p->lock(); return p; }
    alignas(std::mutex) char buf[sizeof(std::mutex)];
    std::mutex *m = lockedMutexFactory(buf);
    use(*m);
    m->~mutex();
but what I really want to write is
    std::mutex lockedMutexFactory() { std::mutex m; m.lock(); return /*somehow*/ m; }
    use(lockedMutexFactory());
 
Now, I don't think any of the half-proposed ideas in this thread so far have been good.
But I disagree with the notion that we should somehow desire two-stage initialization just because one of our types happens to be immovable.
 
FVG wrote:
   optional<mutex> om;

   om.emplace( SomeFunctionThatReturnsMutex() );

Currently we can't do this, and it's something that needs to change.
We can do this fine; use the SCSE pattern.
The things we currently can't do are:
- Implement SomeFunctionThatReturnsMutex() in the first place, unless it's happy with URVO (i.e. just calls a ctor, or another factory, and does nothing else before returning)
- Do this dance for a hypothetical immovable type that is convertible-from any T
 
my $.02,
Arthur
--   Std-Proposals mailing list  Std-Proposals@lists.isocpp.org  https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals  
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
-- 
 Std-Proposals mailing list
 Std-Proposals@lists.isocpp.org
 https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals