You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[SUGGESTION] Confusing std::vector ctor calls in Cpp1 and Cpp2 #193
#include <vector>
using namespace ::std;
int main() {
auto a = vector<int>{5, 1}; // vector of five and one
auto b = vector<int>(5, 1); // vector of five ones
}
Experimenting with (pure) Cpp2 I got a similar case
main: () -> int = {
a: std::vector<int> = (5, 1); // vector of five and one
b := std::vector<int>(5, 1); // vector of five ones
}
This becomes clear when looking at the tanspiled code
std::vector<int> a { 5, 1 }; // vector of five and one
auto b { std::vector<int>(5, 1) }; // vector of five ones
For me the Cpp2 case looks even more confusing (than the Cpp1 case), because both ctor calls use parenthesis.
IMO this would be fairly difficult to solve while keeping full compatibility with Cpp1, the issue is that in C++, the aggregate (i.e. {}) constructor favors initializer lists, while the "classic" parentheses never generate initializer lists.
So, this would either require a change in constructors of std::vector (and other containers too) to outright remove the size & value constructor, or use some kind of tag like with std::in_place_t to disambiguate overloads; or to forego the initializer-list/aggregate constructor form in Cpp2, which would prevent the ambiguity, but also disable initializer lists.
Honestly, it would be easier if C++ just left {} for initializer lists, and only use () for constructors. That way there would be no ambiguity, if you saw std::vector v = {0, 1}, you'd know it is definitely an initializer list, and the vector contains 0 and 1, instead of 1 zero.
Reacted by Mario Bras, Robert Shepherd, sclel016 and Jorge Canizales
Honestly, it would be easier if C++ just left {} for initializer lists, and only use () for constructors. That way there would be no ambiguity, if you saw std::vector v = {0, 1}, you'd know it is definitely an initializer list, and the vector contains 0 and 1, instead of 1 zero.
This would be really nice. An alternative idea could be to use an explicit cast for initializer lists.
The idea is that the parens in v2's initializer make it explicit that we are constructing with a single argument that is a list. (Do you "see" it visually, that the outer parens now clearly contain only a single thing? I think it's visually clear, but I'm curious to learn whether others will see it that way too.)
Honestly, to me that looks just slightly better than manual insertion 😅
I suppose we could just explicitly use braces for initializer lists only, where if {stuff} is encountered in a constructor it would be replaced by ({stuff}), forcing the initializer-list overload.
Then again, this would fall flat for aggregate types such as std::array, as well as the "classic" C-style arrays that do not use std::initializer_list.
print(: std::vector = (5, 1)); // does not transpile
I've been meaning to allow unnamed objects eventually, and seeing you actually trying to write the code pushed me over the edge to implement it now. :)
This now works:
print: (x:_) = {
for x do :(elem:_) = {
std::cout << elem << " ";
}
}
main: ()->int = {
print( : std::vector = (5,1) ); // prints "5 1"
}
Reacted by Filip Sajdak, Sadeq, wolfseifert, Jorge Canizales and Hung Le
Is it possible to infer type from function declaration?
print: (x: std::vector<int>) = {
for x do : (elem: _) = {
std::cout << elem << " ";
}
}
main: () ->int = {
print(: = (5, 1)); // Here
print(: _ = (5, 1)); // Or like this
}
... and from variables declaration:
x :std::vector<int> = (5, 1);
y :std::vector<int> = : std::vector = (5, 1);
z :std::vector<int> = : = (5, 1); // Here
a :std::vector<int> = : _ = (5, 1); // Or like this
print( (5,1) as std::vector ); // for generic functions; prints "5 1"taking_vector( forward (5,1) ); // assuming that this function expects a vector and // that we can deduce the type from the function declaration
For the std::vector "bad API" issue, maybe we should think about correcting it on the cppfront side e.g., like that:
main: () -> int = {
print( std::vector<int>().create(5, 1) ); // prints 1 1 1 1 1// or ...print( create<std::vector>(5, 1) ); // prints 1 1 1 1 1
}
template <typename X>
autocreate(std::vector<X>&&, std::size_t size, X&& value = X{}) -> decltype(auto)
{
return std::vector<X>(size, value);
}
template <template <typename...> classV, typename X>
autocreate(std::size_t size, X&& value = X{}) -> decltype(auto)
{
return V<X>(size, value);
}
print: (x:_) = {
for x do :(elem :_) = {
std::cout << elem << "";
}
std::cout << std::endl;
}
#include<vector>
Names can be adjusted (resize cannot be used instead of create as vector already has a resize method, and UFCS prefers methods over free functions).
Activity
MichaelCook commentedon Jan 2, 2023
Perhaps another way to state this issue is: Should cpp2 do fine-grained removal/repair of unfortunate cpp1 API choices?
robertshepherdcpp commentedon Jan 2, 2023
What about if we had a literal for the size, like
_sso then we could just define the vector like:and then we could do it like this:
JohelEGP commentedon Jan 2, 2023
How would you combine that with existing suffixes like
ULL?filipsajdak commentedon Jan 2, 2023
Soon you will be able to write:
robertshepherdcpp commentedon Jan 2, 2023
@filipsajdak Yes, thats good. Would an overload for a
std::size_tat the end of the vector be possible?fpelliccioni commentedon Jan 2, 2023
I think you confused me with @filipsajdak
JohelEGP commentedon Jan 2, 2023
What about
filipsajdak commentedon Jan 2, 2023
@JohelEGP cheater. 🙂
I agree that there is an issue. I am just trying to advertise the cpp2 casting feature.
What we want to achieve is unambiguous syntax. The spotted issue sure will need some reasonable solution.
switch-blade-stuff commentedon Jan 2, 2023
IMO this would be fairly difficult to solve while keeping full compatibility with Cpp1, the issue is that in C++, the aggregate (i.e.
{}) constructor favors initializer lists, while the "classic" parentheses never generate initializer lists.So, this would either require a change in constructors of
std::vector(and other containers too) to outright remove the size & value constructor, or use some kind of tag like withstd::in_place_tto disambiguate overloads; or to forego the initializer-list/aggregate constructor form in Cpp2, which would prevent the ambiguity, but also disable initializer lists.switch-blade-stuff commentedon Jan 2, 2023
Honestly, it would be easier if C++ just left
{}for initializer lists, and only use()for constructors. That way there would be no ambiguity, if you sawstd::vector v = {0, 1}, you'd know it is definitely an initializer list, and the vector contains 0 and 1, instead of 1 zero.marioarbras commentedon Jan 2, 2023
This would be really nice. An alternative idea could be to use an explicit cast for initializer lists.
robertshepherdcpp commentedon Jan 2, 2023
@marioarbras Or we could have it the other way round, depending on which is more common: initializing a vector with a
std::initializer_listlike:or initializing it with a certain amount of elements like:
Which would be the more common usage? Personally I use the initializer list more.
hsutter commentedon Jan 2, 2023
Yes, fixing that problem is on my list to address as follows (I just haven't implemented it yet):
The idea is that the parens in
v2's initializer make it explicit that we are constructing with a single argument that is a list. (Do you "see" it visually, that the outer parens now clearly contain only a single thing? I think it's visually clear, but I'm curious to learn whether others will see it that way too.)switch-blade-stuff commentedon Jan 2, 2023
Honestly, to me that looks just slightly better than manual insertion 😅
I suppose we could just explicitly use braces for initializer lists only, where if
{stuff}is encountered in a constructor it would be replaced by({stuff}), forcing the initializer-list overload.Then again, this would fall flat for aggregate types such as
std::array, as well as the "classic" C-style arrays that do not usestd::initializer_list.33 remaining items
wolfseifert commentedon Jan 4, 2023
So the only way to pass a
vectorof5and1to a functionprintis like this:Neither
nor
do the trick. Or did I miss something?
jcanizales commentedon Jan 6, 2023
@wolfseifert does
print((5, 1))work?wolfseifert commentedon Jan 7, 2023
No, it does not. I will open a new issue for this later.
Allow unnamed objects (like functions) at expression scope, see #193 …
hsutter commentedon Jan 7, 2023
@wolfseifert:
I've been meaning to allow unnamed objects eventually, and seeing you actually trying to write the code pushed me over the edge to implement it now. :)
This now works:
msadeqhe commentedon Jan 8, 2023
Is it possible to infer type from function declaration?
... and from variables declaration:
wolfseifert commentedon Jan 8, 2023
How I understand this so far:
vector. It is transpiled tostd::vector x {5, 1};std::vector y {std::vector{5, 1}};This variant however makes a lot of sense for something like wheremyvectoris a subclass ofvectorbutymust be of typevector.std::vector z {auto{5, 1}}which does not C++ compile.filipsajdak commentedon Jan 8, 2023
What do you think about that:
For the
std::vector"bad API" issue, maybe we should think about correcting it on the cppfront side e.g., like that:Names can be adjusted (
resizecannot be used instead ofcreateasvectoralready has aresizemethod, and UFCS prefers methods over free functions).filipsajdak commentedon Jan 8, 2023
I have just realized that syntax for unnamed objects
Follows the same syntax as unnamed functions (aka lambdas):
hsutter commentedon Jan 8, 2023
Re syntax: Yes, in both cases it's the identical syntax as a named object/function, but without the name.
I think we've bottomed out on this for now. Thanks, everyone!
Track expression-list paren tokens (not just positions), see comment …
Allow unnamed objects (like functions) at expression scope, see hsutt…
JohelEGP commentedon May 18, 2023
#284 (comment) means it's possible to call
std::vector<int>'s(int, int)constructor: https://cpp2.godbolt.org/z/Pvj8fj1he.AbhinavK00 commentedon May 19, 2023
That's a nice hack.
But the fact that it took this long to come up with a solution shows we need some kind of change.