Date: Fri, 31 May 2024 15:33:14 +0000
std::elide is a single library feature which not only works with std::optional, std::vector, std::deque, std::list, et cetera but also with innumerable classes, utilities, et cetera provided by user libraries. We standardize this and we get interoperation with all of those.
Going the route of adding a new basis operation requires us to modify all the containers (and non-containers too, you'd need a function-invoking version of std::make_shared and std::make_unique as well) and to have that pattern percolate through the entire user ecosystem just to get what can be transparently enabled with a single library utility.
Your rationale for opposing std::elide seems to be a fixation on the idea that emplacement should exclusively call constructors. This is not the case. Emplacement forwards arguments to an expression which initializes an instance of a type. Such expressions may call constructors, or they may not (in the case of conversion which is what std::elide leverages). This is not a new or novel feature of C++. Emplacement has acted this way since it was added in C++11. Initialization has acted this way since at least C++98.
--Robert
________________________________
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Friday, May 31, 2024 10:47
To: Robert A.H. Leahy <rleahy_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
This is the bottom line, this is what you want:
RVO from a function call to construct the object in place.
That's it.
(others want lazy evaluation, can be addressed in a similar way, boils down to doing the same thing)
All this talk about elision, deduction guides, core features.... they are a distraction. What you want is to call a function and have it rvo.
But the way being proposed, it is done in such a way that you don't actually explicitly write a function call. You have to hijack the template system to pass in a special object, this object is not one that is accepted as a constructor parameter (if it were it wouldn't work) but the code is still written as if it was one. And because it is not an acceptable parameter the compiler is forced to do the next best thing which is to try and cast this special object into the object you actually want. So that it can call the function to do rvo.
Here's a better idea. Just call a function, explicitly, no special type required, you can use a lambda, you can use your own special type that you like, it doesn't go wrong because the call is explicit, no need to had a core language change that affects how templates work, no weird hacks required.
Tell me why you think this alternative is not better than std::elide?
________________________________
From: Robert A.H. Leahy <rleahy_at_[hidden]>
Sent: Friday, May 31, 2024 3:05:06 PM
To: Tiago Freire <tmiguelf_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
I don't understand how you get from using two language features (user overloadable implicit conversion operators and C++17 guaranteed RVO) in concert to "trick[ing] rvo."
--Robert
________________________________
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Friday, May 31, 2024 01:46
To: Robert A.H. Leahy <rleahy_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
There you go, now we are getting there. What you want is to trick rvo.
Why not make that explicit? Are we afraid to run out of function names?
________________________________
From: Robert A.H. Leahy <rleahy_at_[hidden]>
Sent: Friday, May 31, 2024 1:25:09 AM
To: Tiago Freire <tmiguelf_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
Passing an rvalue requires there be some object with storage to bind the reference to. The primary use case of std::elide is for when you want to materialize a prvalue directly into storage (say, for example, to initialize something immovable).
--Robert
On May 30, 2024 19:22, Tiago Freire <tmiguelf_at_[hidden]> wrote:
No, the real issue is that you want to call a function instead of passing an object, and you want to do that using passing an object semantics. You are hoping that compiler magic would figure out what you want without writing what you want.
Ps you can pass an rvalue to the emplace of a std::optional so why is it again that you need an std::elide?
________________________________
From: Robert A.H. Leahy <rleahy_at_[hidden]>
Sent: Friday, May 31, 2024 1:07:29 AM
To: Tiago Freire <tmiguelf_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
As you point out later in your own email emplace does not "forward[] arguments to a constructor." It forwards arguments to an expression which initializes an object. This initialization may call a constructor, or it may call a conversion operator and materialize a prvalue (as in the case of std::elide).
If std::elide is objectionable, then so is using emplacement construction with anything that implicitly converts to the destination type, which is a characterization that I think most people would reject.
You're correct that templated constructors can be greedy and thereby be selected in places that were arguably unintended. Fortunately we have techniques (including concepts) to deal with this so this is only really an issue for wrapper types whose constructors aren't appropriately constrained. This is an issue regardless of whether or not std::elide exists.
The real issue with std::elide is how it interacts with CTAD. But there are also solutions for this which don't involve core language changes.
--Robert
________________________________
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Thursday, May 30, 2024 18:52
To: Robert A.H. Leahy <rleahy_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
> Why would we create a new interface for invocation, and then add it to each and every container type, duplicating each and every API which defers responsibility for construction, when the language gives us a mechanism (this one) that works just fine for the purpose?
I disagree, there's no such mechanism.
What you have is an emplace that forwards arguments to a constructor. What this does is to take advantage of a hole in the template metaprogramming to forward an object that is not one that is accepted by the constructor. It then tricks the language to issue a cast (that happens implicitly) instead which in this case invokes a custom function. But this hackery doesn't work completely, because if the constructor is templated it will just accept the object without trying to cast it, cocking it up.
And because it is cocked up, he now wants to add an exception to the core language, so that templates stop working for this special class in order to cover the cock up.
> Note that there's no difference between deferring construction and using the proposed std::elide, and constructing the class directly.
And if that's the case that begs the question as to why you would to use it in the first place?
This is a very good question, why would you use it in the first place?
Every way that is possible to construct the object, you can do it using emplace. So why do you need it?
Why should I accept this wart?
Going the route of adding a new basis operation requires us to modify all the containers (and non-containers too, you'd need a function-invoking version of std::make_shared and std::make_unique as well) and to have that pattern percolate through the entire user ecosystem just to get what can be transparently enabled with a single library utility.
Your rationale for opposing std::elide seems to be a fixation on the idea that emplacement should exclusively call constructors. This is not the case. Emplacement forwards arguments to an expression which initializes an instance of a type. Such expressions may call constructors, or they may not (in the case of conversion which is what std::elide leverages). This is not a new or novel feature of C++. Emplacement has acted this way since it was added in C++11. Initialization has acted this way since at least C++98.
--Robert
________________________________
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Friday, May 31, 2024 10:47
To: Robert A.H. Leahy <rleahy_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
This is the bottom line, this is what you want:
RVO from a function call to construct the object in place.
That's it.
(others want lazy evaluation, can be addressed in a similar way, boils down to doing the same thing)
All this talk about elision, deduction guides, core features.... they are a distraction. What you want is to call a function and have it rvo.
But the way being proposed, it is done in such a way that you don't actually explicitly write a function call. You have to hijack the template system to pass in a special object, this object is not one that is accepted as a constructor parameter (if it were it wouldn't work) but the code is still written as if it was one. And because it is not an acceptable parameter the compiler is forced to do the next best thing which is to try and cast this special object into the object you actually want. So that it can call the function to do rvo.
Here's a better idea. Just call a function, explicitly, no special type required, you can use a lambda, you can use your own special type that you like, it doesn't go wrong because the call is explicit, no need to had a core language change that affects how templates work, no weird hacks required.
Tell me why you think this alternative is not better than std::elide?
________________________________
From: Robert A.H. Leahy <rleahy_at_[hidden]>
Sent: Friday, May 31, 2024 3:05:06 PM
To: Tiago Freire <tmiguelf_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
I don't understand how you get from using two language features (user overloadable implicit conversion operators and C++17 guaranteed RVO) in concert to "trick[ing] rvo."
--Robert
________________________________
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Friday, May 31, 2024 01:46
To: Robert A.H. Leahy <rleahy_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
There you go, now we are getting there. What you want is to trick rvo.
Why not make that explicit? Are we afraid to run out of function names?
________________________________
From: Robert A.H. Leahy <rleahy_at_[hidden]>
Sent: Friday, May 31, 2024 1:25:09 AM
To: Tiago Freire <tmiguelf_at_[hidden]>
Cc: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
Passing an rvalue requires there be some object with storage to bind the reference to. The primary use case of std::elide is for when you want to materialize a prvalue directly into storage (say, for example, to initialize something immovable).
--Robert
On May 30, 2024 19:22, Tiago Freire <tmiguelf_at_[hidden]> wrote:
No, the real issue is that you want to call a function instead of passing an object, and you want to do that using passing an object semantics. You are hoping that compiler magic would figure out what you want without writing what you want.
Ps you can pass an rvalue to the emplace of a std::optional so why is it again that you need an std::elide?
________________________________
From: Robert A.H. Leahy <rleahy_at_[hidden]>
Sent: Friday, May 31, 2024 1:07:29 AM
To: Tiago Freire <tmiguelf_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
As you point out later in your own email emplace does not "forward[] arguments to a constructor." It forwards arguments to an expression which initializes an object. This initialization may call a constructor, or it may call a conversion operator and materialize a prvalue (as in the case of std::elide).
If std::elide is objectionable, then so is using emplacement construction with anything that implicitly converts to the destination type, which is a characterization that I think most people would reject.
You're correct that templated constructors can be greedy and thereby be selected in places that were arguably unintended. Fortunately we have techniques (including concepts) to deal with this so this is only really an issue for wrapper types whose constructors aren't appropriately constrained. This is an issue regardless of whether or not std::elide exists.
The real issue with std::elide is how it interacts with CTAD. But there are also solutions for this which don't involve core language changes.
--Robert
________________________________
From: Tiago Freire <tmiguelf_at_[hidden]>
Sent: Thursday, May 30, 2024 18:52
To: Robert A.H. Leahy <rleahy_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Subject: Re: [std-proposals] std::elide
> Why would we create a new interface for invocation, and then add it to each and every container type, duplicating each and every API which defers responsibility for construction, when the language gives us a mechanism (this one) that works just fine for the purpose?
I disagree, there's no such mechanism.
What you have is an emplace that forwards arguments to a constructor. What this does is to take advantage of a hole in the template metaprogramming to forward an object that is not one that is accepted by the constructor. It then tricks the language to issue a cast (that happens implicitly) instead which in this case invokes a custom function. But this hackery doesn't work completely, because if the constructor is templated it will just accept the object without trying to cast it, cocking it up.
And because it is cocked up, he now wants to add an exception to the core language, so that templates stop working for this special class in order to cover the cock up.
> Note that there's no difference between deferring construction and using the proposed std::elide, and constructing the class directly.
And if that's the case that begs the question as to why you would to use it in the first place?
This is a very good question, why would you use it in the first place?
Every way that is possible to construct the object, you can do it using emplace. So why do you need it?
Why should I accept this wart?
Received on 2024-05-31 15:33:17