Size deduction for std::array from initializer: Comments on n3824 std::make_array

485 views
Skip to first unread message

Matthew Fioravante

unread,
Mar 9, 2015, 3:17:25 PM3/9/15
to std-pr...@isocpp.org
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3824.htm

It seems that without being able to forward braced initializers this solution to "non-type template argument deduction" via a varidic make function is incomplete.

struct Point { double x; double y; }

//n3824 forces us to do this:
auto a = make_array(Point{1.0, 2.0}, Point{3.0, 4.0});

//Should be able to do something like this:
auto a = make_array<Point>( { 1.0, 2.0 }, { 3.0, 4.0 });

//This would be ok too
auto a = make_array<Point>({{1.0, 2.0}, {3.0, 4.0}});

//This might be ideal
std
::array<Point,/*what goes here?*/> a = {{1.0, 2.0}, {3.0, 4.0}};

//For initialization, C array is still superior
Point a[] = {{1.0, 2.0}, {3.0, 4.0}};

For a large array, typing Point{..} every time is redundant and also makes refactoring more painful.

Are we sure this is the direction we want to go in? Is it really a good thing that initializing a C array and initializing a std::array with size deduction use different syntax with completely different rules? I can initialize a C array with braced initializers but I can't do it with std::array.


Somewhat related, there are also issues with declaring and defining static data members with auto. Since we need to use auto with std::make_array, we also run into this problem:

class A {
 
static constexpr auto a = make_array(1, 2, 3);
};
constexpr decltype(A::a) A::a; //Ugly, but it works

template <typename T>
class B {
 
static constexpr auto b = make_array(1, 2, 3);
};
template <typename T>
constexpr decltype(B<T>::b) B<T>::b; //This is a compiler error (it works on some versions of gcc, doesn't work on clang, and according to standard should fail).

As far as I can tell, there is no standard compliant way to write a definition for B<T>::b without writing the exact type. The current workaround is to define the constants in a non-template base class and inherit publicly.

One solution could be to reuse [] (or any other symbol which doesn't break legacy code):

struct A {
 
static constexpr std::array<Point,[]> a = {{1.0, 2.0}, {3.0, 4.0}}; //Value of [] is deduced by number of initializers
};
constexpr std::array<Point,[]> A::a; //Looks for declaration to deduce [], compiler error if none can be found.

constexpr std::array<Point,[]> b; //Compiler error, deducing [] requires either an initializer or a previous declaration with an initializer.

and if another proposal enables auto in template parameters, we can do this:

std::array<auto,[]> = { 1, 2, 3}; //is std::array<int,3>



Zhihao Yuan

unread,
Mar 9, 2015, 4:15:16 PM3/9/15
to std-pr...@isocpp.org
On Mon, Mar 9, 2015 at 3:17 PM, Matthew Fioravante
<fmatth...@gmail.com> wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3824.htm
>
> It seems that without being able to forward braced initializers this
> solution to "non-type template argument deduction" via a varidic make
> function is incomplete.
>
> struct Point { double x; double y; }
>
> //n3824 forces us to do this:
> auto a = make_array(Point{1.0, 2.0}, Point{3.0, 4.0});
>
> //Should be able to do something like this:
> auto a = make_array<Point>( { 1.0, 2.0 }, { 3.0, 4.0 });
>

The problem of unable to perfect forward braced-init list
is a problem with uniform initialization itself, and applies
to everywhere, not limited to make_array, nor make_*.
If you want to solve it, go solve it.

> Is it really a good
> thing that initializing a C array and initializing a std::array with size
> deduction use different syntax with completely different rules?

Not "completely". While the language can do something
that library cannot, you will always have caveats, in lots
of places. But if we choose not to make a progress, we
are probably having bigger issues.

>
> class A {
> static constexpr auto a = make_array(1, 2, 3);
> };
> constexpr decltype(A::a) A::a; //Ugly, but it works
>
> template <typename T>
> class B {
> static constexpr auto b = make_array(1, 2, 3);
> };
> template <typename T>
> constexpr decltype(B<T>::b) B<T>::b; //This is a compiler error (it works on
> some versions of gcc, doesn't work on clang, and according to standard
> should fail).
>

Sorry I don't know why this doesn't work. More like
clang's problem to me.

> and if another proposal enables auto in template parameters, we can do this:
>
> std::array<auto,[]> = { 1, 2, 3}; //is std::array<int,3>
>

Even with that proposal, it still may not work
because { ... } with auto will very likely be deduced
to std::initializer_list<int>, or simply not supported,
but std::array is required to have no (user-declared)
constructor.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://bit.ly/blog4bsd

Matthew Fioravante

unread,
Mar 9, 2015, 4:42:09 PM3/9/15
to std-pr...@isocpp.org


On Monday, March 9, 2015 at 4:15:16 PM UTC-4, Zhihao Yuan wrote:
On Mon, Mar 9, 2015 at 3:17 PM, Matthew Fioravante
<fmatth...@gmail.com> wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3824.htm
>
> It seems that without being able to forward braced initializers this
> solution to "non-type template argument deduction" via a varidic make
> function is incomplete.
>
> struct Point { double x; double y; }
>
> //n3824 forces us to do this:
> auto a = make_array(Point{1.0, 2.0}, Point{3.0, 4.0});
>
> //Should be able to do something like this:
> auto a = make_array<Point>( { 1.0, 2.0 }, { 3.0, 4.0 });
>

The problem of unable to perfect forward braced-init list
is a problem with uniform initialization itself, and applies
to everywhere, not limited to make_array, nor make_*.
If you want to solve it, go solve it.

Accepting make_array() is assuming that this problem will either be solved in the future or that its good enough having to live with it never being solved. Or is the standards committee ok with something now that might be obsoleted in the future should a better solution be found?
 

> Is it really a good
> thing that initializing a C array and initializing a std::array with size
> deduction use different syntax with completely different rules?

Not "completely".  While the language can do something
that library cannot, you will always have caveats, in lots
of places.  But if we choose not to make a progress, we
are probably having bigger issues.

I'm concerned that we may be taking the wrong direction. If a language feature is invented to initialize std::array (and other similar templates) then make_array() may not be needed at all. A language feature also solves this problem for all possible template classes with non-type template size parameters. The make_array() approach means having to write a new make_X() method for each template class which might need it. Looking at your example implementation, writing ones own make_X() is non-trivial with all of the edge cases covered by type_traits.
 

>
> class A {
>   static constexpr auto a = make_array(1, 2, 3);
> };
> constexpr decltype(A::a) A::a; //Ugly, but it works
>
> template <typename T>
> class B {
>   static constexpr auto b = make_array(1, 2, 3);
> };
> template <typename T>
> constexpr decltype(B<T>::b) B<T>::b; //This is a compiler error (it works on
> some versions of gcc, doesn't work on clang, and according to standard
> should fail).
>

Sorry I don't know why this doesn't work.  More like
clang's problem to me.


From what I've found in my research (below bug report and an SO question which I cannot find now), this is standard mandated behavior. Gcc is wrong to accept it.

http://lists.cs.uiuc.edu/pipermail/llvmbugs/2013-January/026679.html

This issue however might be fixable with its own proposal.
 
> and if another proposal enables auto in template parameters, we can do this:
>
> std::array<auto,[]> = { 1, 2, 3}; //is std::array<int,3>
>

Even with that proposal, it still may not work
because { ... } with auto will very likely be deduced
to std::initializer_list<int>, or simply not supported,
but std::array is required to have no (user-declared)
constructor.

This is not really a problem. If you just write a braced initializer and say auto the compiler of course has no idea what you want. If you want points you must say Point somewhere (hopefully only once).

Zhihao Yuan

unread,
Mar 9, 2015, 5:41:51 PM3/9/15
to std-pr...@isocpp.org
On Mon, Mar 9, 2015 at 4:42 PM, Matthew Fioravante
<fmatth...@gmail.com> wrote:
>
> Accepting make_array() is assuming that this problem will either be solved
> in the future or that its good enough having to live with it never being
> solved.

Nope. It simply means the new addition solves the problem
it's trying to solve.

> Or is the standards committee ok with something now that might be
> obsoleted in the future should a better solution be found?
>

No either, because make_array has its unique use cases.
For example, a language level solution cannot produce a
common type out of a braced-init:

std::array<auto,[]> a = { 'a', 10 }; // boom
auto a = make_array('a', 10);

>
> I'm concerned that we may be taking the wrong direction. If a language
> feature is invented to initialize std::array (and other similar templates)
> then make_array() may not be needed at all.

It's committee's job to decide direction. I think we have
done with the technical issues here.

> The make_array() approach means having to write a new make_X()
> method for each template class which might need it.

Short, flexible, and works for me.

> Looking at your example
> implementation, writing ones own make_X() is non-trivial with all of the
> edge cases covered by type_traits.
>

You might want to take a closer look. They are "features"
which cannot be provided with language extensions.
Reply all
Reply to author
Forward
0 new messages