Date: Fri, 5 Sep 2025 23:35:44 -0400
> a prvalue is certain to end its lifetime inside the body of the callee, while
> an xrvalue doesn't.
No, the value category isn't going to change the lifetime here. What matters is
whether the function receives the argument by value or by reference. When it's
by value, it is implementation-defined whether the object is destroyed at the
end of the call or at the end of the full expression.
> program binary size can be reduced.
Only on some ABIs, some will call the destructor of the parameters in the
caller.
> exception wise: if the destructor of T throws an exception, then it is thrown
> in the context of the caller not the callee which is counter intuitive.
> Because a temporary exists only inside the callee.
Firstly, throwing destructors is a bad idea. Secondly, what you're describing as
intuitive is not possible. A parameter is destroyed in the context of the caller
so when taking it by value it will still work in the way you find unintuitive.
The only way in which this would matter is if you had a try block in the
function to catch the exception, but I don't see why one would want to do this.
> I don't see this statement in the example!
Yes, I didn't write any statement because there is no valid way to do so.
> > since to achieve something like that would mean the caller of bar would
> > somehow need to know where bar will forward it to construct the object
> > correctly.
> I didn't understand this statement.
My point is that if there were some way to forward the argument to foo, then the
caller of bar must be aware of that. Because there is no way to relocate the S,
the caller of bar must create the object in the correct location for the call to
foo.
> Logical and intuitively: T is not T&&
> T is about to die in the scope it was created.
> T&& is not.
> Calling foo:
> foo(T{}) <---1
> Should be different than calling
> foo(std::move(t_val)) <--2
How about foo(std::move(T{}))? Or:
std::optional<T>bar();
foo(*bar());
It isn't that uncommon to have xvalues that refer to temporary objects.
> This has an impact on the assembly that would be generated if we can make a
> difference between xvalue and prvalue.
If this is about where the destructor is called, I don't think that is
important. Also, have you considered that splitting up xvalue and prvalues would
likely result in more instantiations for templates being generated? It seems
more likely that the binary size would actually increase because of that.
On Fri, Sep 5, 2025 at 9:59 PM organicoman <organicoman_at_[hidden]> wrote:
>
>
>
>
> > There is no way to tell, from inside the function block, if the parameter
> > passed to this function is a prvalue or an xrvalue
>
> I assume that you meant to say xvalue instead of xrvalue.
>
> Your assumption is correct.
>
> The reason they
> can't be distinguished is that it's not possible to forward a prvalue. Consider:
>
> struct S{
> S();
> S(S&&)=delete;
> };
> void foo(S);
> void bar(S s){
> //...
> }
>
> The class S cannot be copied nor moved.
>
> Correct
>
> There is no mechanism currently that
> would allow for forwarding bar's parameter to foo,
>
> I don't see this statement in the example!
> But I assume you mean this:
> void bar (S s){
> foo(s); // compilation error
> }
>
> If so, then from foo signature it is clear that you are asking for temporary, not lvalue ref nor an rvalue ref.
>
> since to achieve something
> like that would mean the caller of bar would somehow need to know where bar will
> forward it to construct the object correctly.
>
> I didn't understand this statement.
>
> When taking a prvalue of type T there are only two types of parameters that
> would make sense: T and T&&. As described above, when the parameter has type T
> it can still only be forwarded as an xvalue. So, when just forwarding the T
> there generally won't be any differences. The parameter having type T&& has a
> few advantages though. If a reference to the object is returned, the caller can
> use that object inside of the full expression which the call is in. When the
> parameter has type T it might be destroyed at the end of the call. For generic
> code that forwards arbitrary types, using T&& simplifies code since it can rely
> on reference collapsing and that the parameter has a reference type.
>
> Logical and intuitively: T is not T&&
> T is about to die in the scope it was created.
> T&& is not.
> Calling foo:
> foo(T{}) <---1
> Should be different than calling
> foo(std::move(t_val)) <--2
>
> Inside call 1, I can still the parameter resource since I'm certain it is about to end lifetime.
> But inside call 2, I'm not sure.
> This has an impact on the assembly that would be generated if we can make a difference between xvalue and prvalue.
>
> On Fri, Sep 5, 2025 at 8:18 PM organicoman via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > Hello,
> > Given the following function signature:
> >
> > RetType foo(T&& arg);
> >
> > There is no way to tell, from inside the function block, if the parameter passed to this function is a prvalue or an xrvalue, since both can bind to T&&.
> > Despite there is a fundamental difference between prvalue and xrvalue, yet we are not taking advantage of that because we cannot differentiate between them.
> >
> > Is there any proposal talking about this problem?
> >
> > Regards
> > Og
> >
> > --
> > Std-Proposals mailing list
> > Std-Proposals_at_[hidden]
> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> an xrvalue doesn't.
No, the value category isn't going to change the lifetime here. What matters is
whether the function receives the argument by value or by reference. When it's
by value, it is implementation-defined whether the object is destroyed at the
end of the call or at the end of the full expression.
> program binary size can be reduced.
Only on some ABIs, some will call the destructor of the parameters in the
caller.
> exception wise: if the destructor of T throws an exception, then it is thrown
> in the context of the caller not the callee which is counter intuitive.
> Because a temporary exists only inside the callee.
Firstly, throwing destructors is a bad idea. Secondly, what you're describing as
intuitive is not possible. A parameter is destroyed in the context of the caller
so when taking it by value it will still work in the way you find unintuitive.
The only way in which this would matter is if you had a try block in the
function to catch the exception, but I don't see why one would want to do this.
> I don't see this statement in the example!
Yes, I didn't write any statement because there is no valid way to do so.
> > since to achieve something like that would mean the caller of bar would
> > somehow need to know where bar will forward it to construct the object
> > correctly.
> I didn't understand this statement.
My point is that if there were some way to forward the argument to foo, then the
caller of bar must be aware of that. Because there is no way to relocate the S,
the caller of bar must create the object in the correct location for the call to
foo.
> Logical and intuitively: T is not T&&
> T is about to die in the scope it was created.
> T&& is not.
> Calling foo:
> foo(T{}) <---1
> Should be different than calling
> foo(std::move(t_val)) <--2
How about foo(std::move(T{}))? Or:
std::optional<T>bar();
foo(*bar());
It isn't that uncommon to have xvalues that refer to temporary objects.
> This has an impact on the assembly that would be generated if we can make a
> difference between xvalue and prvalue.
If this is about where the destructor is called, I don't think that is
important. Also, have you considered that splitting up xvalue and prvalues would
likely result in more instantiations for templates being generated? It seems
more likely that the binary size would actually increase because of that.
On Fri, Sep 5, 2025 at 9:59 PM organicoman <organicoman_at_[hidden]> wrote:
>
>
>
>
> > There is no way to tell, from inside the function block, if the parameter
> > passed to this function is a prvalue or an xrvalue
>
> I assume that you meant to say xvalue instead of xrvalue.
>
> Your assumption is correct.
>
> The reason they
> can't be distinguished is that it's not possible to forward a prvalue. Consider:
>
> struct S{
> S();
> S(S&&)=delete;
> };
> void foo(S);
> void bar(S s){
> //...
> }
>
> The class S cannot be copied nor moved.
>
> Correct
>
> There is no mechanism currently that
> would allow for forwarding bar's parameter to foo,
>
> I don't see this statement in the example!
> But I assume you mean this:
> void bar (S s){
> foo(s); // compilation error
> }
>
> If so, then from foo signature it is clear that you are asking for temporary, not lvalue ref nor an rvalue ref.
>
> since to achieve something
> like that would mean the caller of bar would somehow need to know where bar will
> forward it to construct the object correctly.
>
> I didn't understand this statement.
>
> When taking a prvalue of type T there are only two types of parameters that
> would make sense: T and T&&. As described above, when the parameter has type T
> it can still only be forwarded as an xvalue. So, when just forwarding the T
> there generally won't be any differences. The parameter having type T&& has a
> few advantages though. If a reference to the object is returned, the caller can
> use that object inside of the full expression which the call is in. When the
> parameter has type T it might be destroyed at the end of the call. For generic
> code that forwards arbitrary types, using T&& simplifies code since it can rely
> on reference collapsing and that the parameter has a reference type.
>
> Logical and intuitively: T is not T&&
> T is about to die in the scope it was created.
> T&& is not.
> Calling foo:
> foo(T{}) <---1
> Should be different than calling
> foo(std::move(t_val)) <--2
>
> Inside call 1, I can still the parameter resource since I'm certain it is about to end lifetime.
> But inside call 2, I'm not sure.
> This has an impact on the assembly that would be generated if we can make a difference between xvalue and prvalue.
>
> On Fri, Sep 5, 2025 at 8:18 PM organicoman via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > Hello,
> > Given the following function signature:
> >
> > RetType foo(T&& arg);
> >
> > There is no way to tell, from inside the function block, if the parameter passed to this function is a prvalue or an xrvalue, since both can bind to T&&.
> > Despite there is a fundamental difference between prvalue and xrvalue, yet we are not taking advantage of that because we cannot differentiate between them.
> >
> > Is there any proposal talking about this problem?
> >
> > Regards
> > Og
> >
> > --
> > Std-Proposals mailing list
> > Std-Proposals_at_[hidden]
> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2025-09-06 03:36:18