Date: Tue, 4 Jun 2024 15:39:36 +0100
On 03/06/2024 22:35, Frederick Virchanza Gotham via Std-Proposals wrote:
> On Fri, May 31, 2024 at 4:12 PM Thiago Macieira wrote:
>>
>> A prvalue is a special type of rvalue. What you're saying is that rvalue-ref
>> functions can only accept xvalues but not prvalues, though that's patently
>> false.
> <snip>
>> Given that your introduction section contains such a blatant absurdity, either
>> your paper is entirely wrong or you're using wrong terms to mean what you want
>> to mean. I didn't read the rest.
>
>
> I've corrected the wording and amended the paper. Here it is:
>
> http://www.virjacode.com/papers/prvalue_params.htm
>
> Here's the list of changes:
>
> * Syntax changed from ^^ to &&&.
> * Wording of value categories corrected (i.e. XRvalue, PRvalue).
> * Implementation in x86_64 assembler for Linux, Apple, FreeBSD.
The proposed change as motivated by optional::emplace breaks the
following example:
https://godbolt.org/z/ejYT4jG5K
void foo(std::optional<std::string>& opt_str) {
if (opt_str.has_value()) {
opt_str.emplace(
std::move(*opt_str) + " World!"
);
}
}
The example is a bit tortured with std::optional<std::string>, but would
probably make more sense with a `std::optinal<HardToMutate>` that's
easier to mutate by emplacing over than through non-const member functions.
More generally if you call a member function on an object with a
"deferred" argument, and you access the same object in the same argument
then you can get into reentrancy issues.
The same applies for `std::elide` or `emplace_invoke` too, but there it
is more explicit that the evaluation of the lambda's body is deferred.
It also doesn't break existing correct code.
Having said that I think functions with deferred arguments have their
upsides too, namely it actually provides perfect forwarding that can
potentially forward prvalues, or even overload sets, braced initializer
lists and null pointer constants. All the while keeping the same
interface as the function you are forwarding to, which can be useful in
generic contexts for satisfying constraints.
I would make operator() of the "functor" that represents the argument to
be && qualified, to deter from calling it multiple times in the function.
You should also read up on
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0927r2.pdf, as
that's a very similar proposal.
>
>> I did notice you're still using std::mutex in your examples, though. STOP
>> DOING THAT. Anyone reading the paper will again go into the discussion of how
>> returning a mutex is pointless. Find another, real type that is immovable but
>> makes sense to return.
>
>
> I've been able to devise a regex to detect the people who dislike the
> locked mutex example:
>
> T[h]{0,1}iago
>
> I'm not willing to adjust the parameters of common sense to
> accommodate two people who cannot see the woods for the trees.
>
> Thiago, As árvores não deixam ver a floresta.
> Tiago, Ik zie door het bos de bomen niet meer.
>
> Anyway . . . back to talking about the class, std::elide. If we don't
> include the change to the core language, then it will work with some
> classes and not with others -- and that's not good enough in my
> opinion. It should work with all classes, even the ones which have an
> accept-anything constructor.
>
> We need to devise some sort of way of preventing an "std::elide"
> object from becoming an argument to a constructor which has exactly
> one parameter. My paper suggests adding a short paragraph under "When
> template instantiation will fail", however there are other
> possibilities, such as marking the conversion operator:
>
> template<typename T>
> class elide {
> operator T() priority
> {
> }
> };
>
> When the compiler sees a conversion operator marked as 'priority', it
> invokes the conversion operator instead of allowing the class itself
> to become the parameter to a constructor. I'm just throwing ideas out
> there. Anyone got any other ideas for how to prevent std::elide from
> becoming a constructor parameter?
> On Fri, May 31, 2024 at 4:12 PM Thiago Macieira wrote:
>>
>> A prvalue is a special type of rvalue. What you're saying is that rvalue-ref
>> functions can only accept xvalues but not prvalues, though that's patently
>> false.
> <snip>
>> Given that your introduction section contains such a blatant absurdity, either
>> your paper is entirely wrong or you're using wrong terms to mean what you want
>> to mean. I didn't read the rest.
>
>
> I've corrected the wording and amended the paper. Here it is:
>
> http://www.virjacode.com/papers/prvalue_params.htm
>
> Here's the list of changes:
>
> * Syntax changed from ^^ to &&&.
> * Wording of value categories corrected (i.e. XRvalue, PRvalue).
> * Implementation in x86_64 assembler for Linux, Apple, FreeBSD.
The proposed change as motivated by optional::emplace breaks the
following example:
https://godbolt.org/z/ejYT4jG5K
void foo(std::optional<std::string>& opt_str) {
if (opt_str.has_value()) {
opt_str.emplace(
std::move(*opt_str) + " World!"
);
}
}
The example is a bit tortured with std::optional<std::string>, but would
probably make more sense with a `std::optinal<HardToMutate>` that's
easier to mutate by emplacing over than through non-const member functions.
More generally if you call a member function on an object with a
"deferred" argument, and you access the same object in the same argument
then you can get into reentrancy issues.
The same applies for `std::elide` or `emplace_invoke` too, but there it
is more explicit that the evaluation of the lambda's body is deferred.
It also doesn't break existing correct code.
Having said that I think functions with deferred arguments have their
upsides too, namely it actually provides perfect forwarding that can
potentially forward prvalues, or even overload sets, braced initializer
lists and null pointer constants. All the while keeping the same
interface as the function you are forwarding to, which can be useful in
generic contexts for satisfying constraints.
I would make operator() of the "functor" that represents the argument to
be && qualified, to deter from calling it multiple times in the function.
You should also read up on
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0927r2.pdf, as
that's a very similar proposal.
>
>> I did notice you're still using std::mutex in your examples, though. STOP
>> DOING THAT. Anyone reading the paper will again go into the discussion of how
>> returning a mutex is pointless. Find another, real type that is immovable but
>> makes sense to return.
>
>
> I've been able to devise a regex to detect the people who dislike the
> locked mutex example:
>
> T[h]{0,1}iago
>
> I'm not willing to adjust the parameters of common sense to
> accommodate two people who cannot see the woods for the trees.
>
> Thiago, As árvores não deixam ver a floresta.
> Tiago, Ik zie door het bos de bomen niet meer.
>
> Anyway . . . back to talking about the class, std::elide. If we don't
> include the change to the core language, then it will work with some
> classes and not with others -- and that's not good enough in my
> opinion. It should work with all classes, even the ones which have an
> accept-anything constructor.
>
> We need to devise some sort of way of preventing an "std::elide"
> object from becoming an argument to a constructor which has exactly
> one parameter. My paper suggests adding a short paragraph under "When
> template instantiation will fail", however there are other
> possibilities, such as marking the conversion operator:
>
> template<typename T>
> class elide {
> operator T() priority
> {
> }
> };
>
> When the compiler sees a conversion operator marked as 'priority', it
> invokes the conversion operator instead of allowing the class itself
> to become the parameter to a constructor. I'm just throwing ideas out
> there. Anyone got any other ideas for how to prevent std::elide from
> becoming a constructor parameter?
Received on 2024-06-04 14:39:47