Initializing arrays using non-default constructors

273 views
Skip to first unread message

Avi Kivity

unread,
Apr 8, 2018, 12:48:50 PM4/8/18
to ISO C++ Standard - Future Proposals
If I have an array


    foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators), we should have a way to initialize arrays using
user-defined constructors.


We might allow something like


    foo a[10000] = std::by_element(some_iterator);


The user supplied an input iterator, whose operator* returns a tuple,
and that tuple is used to to feed the constructor. For example



    int a[10000] =
std::by_element(boost::make_transform_iterator(boost::make_counting_iterator(0),
[] (int x) { return std::tuple(x); }));


will initialize the array to the sequence 0..9999. We might relax the
constraints to allow non-tuples, in which case the result of operator*
is def to a single-argument constructor of the array element type:


    std::array<foo, N> xform(std::array<bar, N> a) {

          return std::array<foo, N>(std::by_element(a));

    }


calls foo::foo(bar&).


Currently, we can solve the problem with std::aligned_storage and
placement new, but that's quite an ugly workaround.


dgutson .

unread,
Apr 8, 2018, 12:52:03 PM4/8/18
to std-pr...@isocpp.org


El dom., 8 de abril de 2018 13:48, Avi Kivity <a...@scylladb.com> escribió:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.

Long ago in a galaxy far far away...






Since the trend is not to require default constructors (or move/copy
assignment operators), we should have a way to initialize arrays using
user-defined constructors.


We might allow something like


     foo a[10000] = std::by_element(some_iterator);


The user supplied an input iterator, whose operator* returns a tuple,
and that tuple is used to to feed the constructor. For example



     int a[10000] =
std::by_element(boost::make_transform_iterator(boost::make_counting_iterator(0),
[] (int x) { return std::tuple(x); }));


will initialize the array to the sequence 0..9999. We might relax the
constraints to allow non-tuples, in which case the result of operator*
is def to a single-argument constructor of the array element type:


     std::array<foo, N> xform(std::array<bar, N> a) {

           return std::array<foo, N>(std::by_element(a));

     }


calls foo::foo(bar&).


Currently, we can solve the problem with std::aligned_storage and
placement new, but that's quite an ugly workaround.


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/64e29521-ac56-f324-7140-23e824f418d8%40scylladb.com.

Avi Kivity

unread,
Apr 8, 2018, 1:01:43 PM4/8/18
to std-pr...@isocpp.org, dgutson .



On 04/08/2018 07:51 PM, dgutson . wrote:


El dom., 8 de abril de 2018 13:48, Avi Kivity <a...@scylladb.com> escribió:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.

Long ago in a galaxy far far away...



Interesting! Nicer and simpler than my proposal.

Did it get any traction?

dgutson .

unread,
Apr 8, 2018, 1:06:27 PM4/8/18
to Avi Kivity, std-pr...@isocpp.org
I had no chance to present it. I had to choose 3 proposals (I wrote 13 for that meeting). One of them later led to the initializer list. I could only come back to a wg21 meeting 10 years later with a different goal.



Avi Kivity

unread,
Apr 8, 2018, 1:10:07 PM4/8/18
to dgutson ., std-pr...@isocpp.org
Well, I hope it makes it into the language one day. It's not a huge problem, but it's a wart, and your proposal fixes it very neatly.

Nicol Bolas

unread,
Apr 8, 2018, 1:32:33 PM4/8/18
to ISO C++ Standard - Future Proposals


On Sunday, April 8, 2018 at 12:48:50 PM UTC-4, Avi Kivity wrote:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators),

Wait, there's a trend towards that? I certainly don't see it in the standard library. `variant` in particular practically bends over backwards to allow default construction.

The trend is to avoiding uninitialization. If `foo` had a proper default constructor, then none of the elements in that array will go uninitialized. And you can avoid leaving things uninitialized even if `foo` has trivial default construction by doing `foo a[10000] = {};`.

The idea of initializing an array by repeatedly calling some function is not without merit. The problem is that it doesn't work for `std::array`. And we really would rather people use that than language arrays.

dgutson .

unread,
Apr 8, 2018, 1:51:31 PM4/8/18
to std-pr...@isocpp.org
Why fixing that would be a problem? E.g. a special class&& receiving something like a lambda or std::function as constructor argument for being itself an argumeny for std::array ctor. By special I mean a specific std class, such as nothrow_t.

auto i=0u;
std::array<X, Y> arr(std::constructor_arg ([&i] (return ++i;)));


. And we really would rather people use that than language arrays.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Apr 8, 2018, 3:52:14 PM4/8/18
to ISO C++ Standard - Future Proposals


On Sunday, April 8, 2018 at 1:51:31 PM UTC-4, dgutson wrote:


El dom., 8 de abril de 2018 14:32, Nicol Bolas <jmck...@gmail.com> escribió:


On Sunday, April 8, 2018 at 12:48:50 PM UTC-4, Avi Kivity wrote:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators),

Wait, there's a trend towards that? I certainly don't see it in the standard library. `variant` in particular practically bends over backwards to allow default construction.

The trend is to avoiding uninitialization. If `foo` had a proper default constructor, then none of the elements in that array will go uninitialized. And you can avoid leaving things uninitialized even if `foo` has trivial default construction by doing `foo a[10000] = {};`.

The idea of initializing an array by repeatedly calling some function is not without merit. The problem is that it doesn't work for `std::array`

Why fixing that would be a problem? E.g. a special class&& receiving something like a lambda or std::function as constructor argument for being itself an argumeny for std::array ctor. By special I mean a specific std class, such as nothrow_t.

auto i=0u;
std::array<X, Y> arr(std::constructor_arg ([&i] (return ++i;)));

Because this would require `array` to have constructors. And it can't do that if it's going to remain an aggregate. And you can't just make it not an aggregate anymore, since there's tons of code out there which relies on it using aggregate initialization.

So if there's going to be something like this, it needs to be tied into aggregate initialization as a mechanism. And that's really hard to do, since AI can be used on heterogeneous structs, not just homogeneous arrays.

Alberto Barbati

unread,
Apr 9, 2018, 5:43:08 AM4/9/18
to ISO C++ Standard - Future Proposals, daniel...@gmail.com
Il giorno domenica 8 aprile 2018 19:10:07 UTC+2, Avi Kivity ha scritto:
Well, I hope it makes it into the language one day. It's not a huge problem, but it's a wart, and your proposal fixes it very neatly.


I don't think we need a language facility for that. This scenario could be addressed with special library function:

template <class T, size_t N, class InputIterator>
std::array<T, N> make_array(InputIterator values);

This has to be a library function, because the implementation needs, possibly exploiting compiler magic, to ensure that:

1) the array is created *without* calling the default constructors of its elements, even if they would be used: elements are then initialized with std::uninitialized_copy_n
2) the constructed array is returned with guaranteed copy elision (this may be the hard part, since this is probably a case of NRVO where the elision may occur, but it's not guaranteed)

Since we have a parallel version of std::uninitialized_copy_n, we might even have a parallel version of make_array.

Avi Kivity

unread,
Apr 9, 2018, 9:23:44 AM4/9/18
to std-pr...@isocpp.org, Alberto Barbati, daniel...@gmail.com
Minor problems in a library implementation:
 - I think it will be hard to make it constexpr, and such a low-level facility should support constexpr
 - it still requires move construction, which may not be possible for classes whose constructors capture 'this' (for example, in a lambda that gets registered somewhere).

Avi Kivity

unread,
Apr 9, 2018, 9:25:23 AM4/9/18
to std-pr...@isocpp.org, Nicol Bolas



On 2018-04-08 20:32, Nicol Bolas wrote:


On Sunday, April 8, 2018 at 12:48:50 PM UTC-4, Avi Kivity wrote:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators),

Wait, there's a trend towards that? I certainly don't see it in the standard library. `variant` in particular practically bends over backwards to allow default construction.

The trend is towards not requiring default constructors in user types.



The trend is to avoiding uninitialization. If `foo` had a proper default constructor, then none of the elements in that array will go uninitialized. And you can avoid leaving things uninitialized even if `foo` has trivial default construction by doing `foo a[10000] = {};`.


Not all classes can have a reasonable default constructor (for example, classes with reference members).

Alberto Barbati

unread,
Apr 9, 2018, 9:45:52 AM4/9/18
to ISO C++ Standard - Future Proposals, alberto...@gmail.com, daniel...@gmail.com

Il giorno lunedì 9 aprile 2018 15:23:44 UTC+2, Avi Kivity ha scritto:


Minor problems in a library implementation:
 - I think it will be hard to make it constexpr, and such a low-level facility should support constexpr


I agree that having it constexpr would be useful. I'm not convinced it would be hard to get, what makes you so sure?

 - it still requires move construction, which may not be possible for classes whose constructors capture 'this' (for example, in a lambda that gets registered somewhere).



I don't see where it would require a move construction. Could you please elaborate? (Notice that I assume we can have both guaranteed NRVO and guaranteed copy elision. Without either of them, the approach loses most of its appeal.)

A.

PS: I forgot to mention that we could also have a variant using uninitialized_move_n instead of uninitialized_copy_n. 

Avi Kivity

unread,
Apr 9, 2018, 9:53:19 AM4/9/18
to std-pr...@isocpp.org, Alberto Barbati, daniel...@gmail.com



On 2018-04-09 16:45, Alberto Barbati wrote:

Il giorno lunedì 9 aprile 2018 15:23:44 UTC+2, Avi Kivity ha scritto:


Minor problems in a library implementation:
 - I think it will be hard to make it constexpr, and such a low-level facility should support constexpr


I agree that having it constexpr would be useful. I'm not convinced it would be hard to get, what makes you so sure?

I'm not sure at all. I'm just not optimistic that all the union+placement new stuff would be constexpr.



 - it still requires move construction, which may not be possible for classes whose constructors capture 'this' (for example, in a lambda that gets registered somewhere).



I don't see where it would require a move construction. Could you please elaborate? (Notice that I assume we can have both guaranteed NRVO and guaranteed copy elision. Without either of them, the approach loses most of its appeal.)


We can't construct the return value as a local since it would involve default construction. So we have to have some kind of union, from which the result would be moved.

std::array<T, N> make_array(Iterator i ) {
    std::array<T, N> ret = ?;
    ...
    return ret;

}


A.

PS: I forgot to mention that we could also have a variant using uninitialized_move_n instead of uninitialized_copy_n. 


Those are also not constexpr, perhaps due to the placement new requirement.

Nicol Bolas

unread,
Apr 9, 2018, 10:52:44 AM4/9/18
to ISO C++ Standard - Future Proposals, alberto...@gmail.com, daniel...@gmail.com
On Monday, April 9, 2018 at 9:53:19 AM UTC-4, Avi Kivity wrote:



On 2018-04-09 16:45, Alberto Barbati wrote:

Il giorno lunedì 9 aprile 2018 15:23:44 UTC+2, Avi Kivity ha scritto:


Minor problems in a library implementation:
 - I think it will be hard to make it constexpr, and such a low-level facility should support constexpr


I agree that having it constexpr would be useful. I'm not convinced it would be hard to get, what makes you so sure?

I'm not sure at all. I'm just not optimistic that all the union+placement new stuff would be constexpr.

The whole point of making it a standard library function is that it doens't have to be implemented with "all the union+placement new stuff". It works because the standard says that it works. It will be `constexpr` by definition.

Nicol Bolas

unread,
Apr 9, 2018, 11:00:49 AM4/9/18
to ISO C++ Standard - Future Proposals, jmck...@gmail.com


On Monday, April 9, 2018 at 9:25:23 AM UTC-4, Avi Kivity wrote:



On 2018-04-08 20:32, Nicol Bolas wrote:


On Sunday, April 8, 2018 at 12:48:50 PM UTC-4, Avi Kivity wrote:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators),

Wait, there's a trend towards that? I certainly don't see it in the standard library. `variant` in particular practically bends over backwards to allow default construction.

The trend is towards not requiring default constructors in user types.

Can you point to some evidence for this trend? Because I don't see a great increase in the number of types without default constructors in C++. Indeed, the range concepts use Semiregular everywhere, and Semiregular, requires DefaultConstructible.

So I see plenty of evidence that DefaultConstructible remains a common expectation for user types.

Richard Hodges

unread,
Apr 9, 2018, 11:53:51 AM4/9/18
to std-pr...@isocpp.org
FWIW 1 data point here.

In my code, types are only ever default-constructible if they are useful if constructed as such. If I'm deserialising non-default-constructible objects or storing them in arrays I use generator functions. I don't tolerate redundant constructions. It's bad form, invites inefficiency, and invites bugs. 

 

So I see plenty of evidence that DefaultConstructible remains a common expectation for user types.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Avi Kivity

unread,
Apr 9, 2018, 11:55:33 AM4/9/18
to std-pr...@isocpp.org, Nicol Bolas, alberto...@gmail.com, daniel...@gmail.com
One of the nice things (IMO) about the standard C++ library is that much of it is implementable with the language itself, without resorting to compiler magic.

If the standard library requires a back-door compiler interface, we might as well find good syntax for it and make it a language feature (as was proposed earlier in the thread, by Daniel).

Avi Kivity

unread,
Apr 9, 2018, 11:56:49 AM4/9/18
to std-pr...@isocpp.org, Richard Hodges



On 2018-04-09 18:53, Richard Hodges wrote:


On 9 April 2018 at 16:00, Nicol Bolas <jmck...@gmail.com> wrote:


On Monday, April 9, 2018 at 9:25:23 AM UTC-4, Avi Kivity wrote:



On 2018-04-08 20:32, Nicol Bolas wrote:


On Sunday, April 8, 2018 at 12:48:50 PM UTC-4, Avi Kivity wrote:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators),

Wait, there's a trend towards that? I certainly don't see it in the standard library. `variant` in particular practically bends over backwards to allow default construction.

The trend is towards not requiring default constructors in user types.

Can you point to some evidence for this trend? Because I don't see a great increase in the number of types without default constructors in C++. Indeed, the range concepts use Semiregular everywhere, and Semiregular, requires DefaultConstructible.

FWIW 1 data point here.

In my code, types are only ever default-constructible if they are useful if constructed as such. If I'm deserialising non-default-constructible objects or storing them in arrays I use generator functions. I don't tolerate redundant constructions. It's bad form, invites inefficiency, and invites bugs. 


Ditto here. If I'm forced to add a default constructor, I'm extremely annoyed. Sometimes I refuse and use an std::optional instead.

Nicol Bolas

unread,
Apr 9, 2018, 12:37:31 PM4/9/18
to ISO C++ Standard - Future Proposals, hodg...@gmail.com

That's all fine, but that doesn't change the fact that, as far as the standard is concerned, default constructors are frequently expected to be available, and this expectation is being encoded into increasingly large parts of the standard library. This is the opposite of "the trend is not to require default constructors". It may be a personal trend among some programmers, but it is not a general C++ trend, and certainly not a trend for the C++ standard library.

You cannot write an iterator that is not default constructible, for example. Even though no sane implementation of an algorithm will bother to use it, it must be there as part of the concept constraint.

I don't think the "avoid default construction/tors" is a good motivation for the proposal. A better one is to avoid unnecessary work. We already have the annoyance of having `vector` value-initialize types, thus frequently provoking multiple initializations if you're just going to copy into the array. Having ways to avoid potentially-expensive default initialization that will quickly be overwritten by the actual data would be a good thing.

Avi Kivity

unread,
Apr 9, 2018, 1:33:41 PM4/9/18
to std-pr...@isocpp.org, Nicol Bolas, hodg...@gmail.com



On 2018-04-09 19:37, Nicol Bolas wrote:
On Monday, April 9, 2018 at 11:56:49 AM UTC-4, Avi Kivity wrote:
On 2018-04-09 18:53, Richard Hodges wrote:


On 9 April 2018 at 16:00, Nicol Bolas <jmck...@gmail.com> wrote:


On Monday, April 9, 2018 at 9:25:23 AM UTC-4, Avi Kivity wrote:



On 2018-04-08 20:32, Nicol Bolas wrote:


On Sunday, April 8, 2018 at 12:48:50 PM UTC-4, Avi Kivity wrote:
If I have an array


     foo a[10000];


I can either let it be initialized with the default constructor, or
choose aggregate initialization and specify initializers for each element.


Since the trend is not to require default constructors (or move/copy
assignment operators),

Wait, there's a trend towards that? I certainly don't see it in the standard library. `variant` in particular practically bends over backwards to allow default construction.

The trend is towards not requiring default constructors in user types.

Can you point to some evidence for this trend? Because I don't see a great increase in the number of types without default constructors in C++. Indeed, the range concepts use Semiregular everywhere, and Semiregular, requires DefaultConstructible.

FWIW 1 data point here.

In my code, types are only ever default-constructible if they are useful if constructed as such. If I'm deserialising non-default-constructible objects or storing them in arrays I use generator functions. I don't tolerate redundant constructions. It's bad form, invites inefficiency, and invites bugs. 


Ditto here. If I'm forced to add a default constructor, I'm extremely annoyed. Sometimes I refuse and use an std::optional instead.


That's all fine, but that doesn't change the fact that, as far as the standard is concerned, default constructors are frequently expected to be available, and this expectation is being encoded into increasingly large parts of the standard library. This is the opposite of "the trend is not to require default constructors". It may be a personal trend among some programmers, but it is not a general C++ trend, and certainly not a trend for the C++ standard library.


I think it is. Can you name new library features that requires default constructors of user-provided types unnecessarily?

Even iterators only require default-constructibility only for a subset (Forward and above), not all.


You cannot write an iterator that is not default constructible, for example. Even though no sane implementation of an algorithm will bother to use it, it must be there as part of the concept constraint.

I don't think the "avoid default construction/tors" is a good motivation for the proposal. A better one is to avoid unnecessary work.

In some cases, default constructors don't make sense, in other cases they are hard to provide. I think that avoiding the need to require them is motivation on its own.

Avoiding unneeded work is distinct motivation.



We already have the annoyance of having `vector` value-initialize types, thus frequently provoking multiple initializations if you're just going to copy into the array. Having ways to avoid potentially-expensive default initialization that will quickly be overwritten by the actual data would be a good thing.


vector doesn't require a default constructor from its value type and won't provoke multiple initialization if used correctly. The proposal brings something that is available in vector to C arrays (and std::array).

Reply all
Reply to author
Forward
0 new messages