This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD status.

### 930. Access to std::array data as built-in array type

Section: 24.3.7 [array] Status: NAD Submitter: Niels Dekker Opened: 2008-11-17 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [array].

View all issues with NAD status.

Discussion:

The Working Draft (N2798) allows access to the elements of `std::array` by its `data()` member function:

> ##### 23.2.1.4 array::data [array.data]

>      T *data();
>      const T *data() const;

> 1. Returns: elems.

Unfortunately, the result of `std::array::data()` cannot be bound to a reference to a built-in array of the type of `array::elems`. And `std::array` provides no other way to get a reference to `array::elems`. This hampers the use of `std::array`, for example when trying to pass its data to a C style API function:

     // Some C style API function.
     void set_path( char (*)[MAX_PATH] );

     std::array<char,MAX_PATH> path;
     set_path( path.data() );  // error
     set_path( &(path.data()) );  // error

Another example, trying to pass the array data to an instance of another C++ class:

     // Represents a 3-D point in space.
     class three_d_point {
     public:
       explicit three_d_point(const double (&)[3]);
     };

     const std::array<double,3> coordinates = { 0, 1, 2 };
     three_d_point point1( coordinates.data() );  // error.
     three_d_point point2( *(coordinates.data()) );  // error.

A user might be tempted to use `std::array::elems` instead, but doing so isn't recommended, because `std::array::elems` is "for exposition only". Note that Boost.Array users might already use `boost::array::elems`, as its documentation doesn't explicitly state that `boost::array::elems` is for exposition only: http://www.boost.org/doc/libs/1_36_0/doc/html/boost/array.html

I can think of three options to solve this issue:

1. Remove the words "exposition only" from the definition of `std::array::elems`, as well as the note saying that "elems is shown for exposition only."
2. Change the signature of `std::array::data()`, so that it would return a reference to the built-in array, instead of a pointer to its first element.
3. Add extra member functions, returning a reference to the built-in array.

Lawrence Crowl wrote me that it might be better to leave `std::array::elems` "for exposition only", to allow alternate representations to allocate the array data dynamically. This might be of interest to the embedded community, having to deal with very limited stack sizes.

The second option, changing the return type of `std::array::data()`, would break backward compatible to current Boost and TR1 implementations, as well as to the other contiguous container (`vector` and `string`) in a very subtle way. For example, the following call to `std::swap` currently swap two locally declared pointers `(data1, data2)`, for any container type `T` that has a `data()` member function. When `std::array::data()` is changed to return a reference, the `std::swap` call may swap the container elements instead.

     template <typename T>
     void func(T& container1, T& container2)
     {
       // Are data1 and data2 pointers or references?
       auto data1 = container1.data();
       auto data2 = container2.data();

       // Will this swap two local pointers, or all container elements?
       std::swap(data1, data2);
     }

The following concept is currently satisfied by all contiguous containers, but it no longer is for `std::array`, when `array::data()` is changed to return a reference (tested on ConceptGCC Alpha 7):

     auto concept ContiguousContainerConcept<typename T>
     {
       typename value_type = typename T::value_type;
       const value_type * T::data() const;
     }

Still it's worth considering having `std::array::data()` return a reference, because it might be the most intuitive option, from a user's point of view. Nicolai Josuttis (who wrote `boost::array`) mailed me that he very much prefers this option.

Note that for this option, the definition of `data()` would also need to be revised for zero-sized arrays, as its return type cannot be a reference to a zero-sized built-in array. Regarding zero-sized array, `data()` could throw an exception. Or there could be a partial specialization of `std::array` where `data()` returns `T*` or gets removed.

Personally I prefer the third option, adding a new member function to `std::array`, overloaded for const and non-const access, returning a reference to the built-in array, to avoid those compatible issues. I'd propose naming the function `std::array::c_array()`, which sounds intuitive to me. Note that `boost::array` already has a `c_array()` member, returning a pointer, but Nicolai told me that this one is only there for historical reasons. (Otherwise a name like `std::array::native_array()` or `std::array::builtin_array()` would also be fine with me.) According to my proposed resolution, a zero-sized `std::array` does not need to have `c_array()`, while it is still required to have `data()` functions.

[ Post Summit: ]

> Alisdair: Don't like p4 suggesting implementation-defined behaviour.

> Walter: What about an explicit conversion operator, instead of adding the new member function?

> Alisdair: Noodling about:

> >     template<size_t N, ValueType T>
> >     struct array
> >     {
> >       T elems[N];

> >     // fantasy code starts here

> >     // crazy decltype version for grins only
> >     //requires True<(N>0)>
> >     //explict operator decltype(elems) & () { return elems; }

> >     // conversion to lvalue ref
> >     requires True<(N>0)>
> >     explict operator T(&)[N] () & { return elems; }

> >     // conversion to const lvalue ref
> >     requires True<(N>0)>
> >     explict operator const T(&)[N] () const & { return elems; }

> >     // conversion to rvalue ref using ref qualifiers
> >     requires True<(N>0)>
> >     explict operator T(&&)[N] () && { return elems; }

> >     // fantasy code ends here

> >     explicit operator bool() { return true; }
> >     };

> This seems legal but odd. Jason Merrill says currently a CWG issue 613 on the non-static data member that fixes the error that current G++ gives for the non-explicit, non-conceptualized version of this. Verdict from human compiler: seems legal.

> Some grumbling about zero-sized arrays being allowed and supported.

> Walter: Would this address the issue? Are we inclined to go this route?

> Alan: What would usage look like?

> >     // 3-d point in space
> >     struct three_d_point
> >     {
> >       explicit three_d_point(const double (&)[3]);
> >     };

> >     void sink(double*);

> >     const std::array<double, 3> coordinates = { 0, 1, 2 };
> >     three_d_point point1( coordinates.data() ); //error
> >     three_d_point point2( *(coordinates.data()) ); // error
> >     three_d_point point3( coordinates ); // yay!

> >     sink(cooridinates); // error, no conversion

> Recommended Open with new wording. Take the required clause and add the explicit conversion operators, not have a `typedef`. At issue still is use `decltype` or use `T[N]`. In favour of using `T[N]`, even though use of `decltype` is specially clever.

[ Post Summit, further discussion in the thread starting with c++std-lib-23215. ]

[ 2009-07 post-Frankfurt (Saturday afternoon group): ]

> The idea to resolve the issue by adding explicit conversion operators was abandoned, because it would be inconvenient to use, especially when passing the array to a template function, as mentioned by Daniel. So we reconsidered the original proposed resolution, which appeared acceptable, except for its proposed changes to 24.3.7.5 [array.zero], which allowed `c_array_type` and `c_array()` to be absent for a zero-sized array. Alisdair argued that such wording would disallow certain generic use cases. New wording for 24.3.7.5 [array.zero] was agreed upon (Howard: and is reflected in the proposed resolution).

> Move to Review

[ 2009-07-31 Alisdair adds: ]

> I will be unhappy voting the proposed resolution for 930 past review until we have implementation experience with reference qualifiers. Specifically, I want to understand the impact of the missing overload for `const &&` (if any.)

> If we think the issue is important enough it might be worthwhile stripping the ref qualifiers for easy progress next meeting, and opening yet another issue to put them back with experience.

> Recommend deferring any decision on splitting the issue until we get LWG feedback next meeting - I may be the lone dissenting voice if others are prepared to proceed without it.

[ 2009-10 Santa Cruz: ]

> Mark as NAD. There was not enough consensus that this was sufficiently useful. There are known other ways to do this, such as small inline conversion functions.

Proposed resolution:

Add to the template definition of array, 24.3.7 [array]/3:

>     typedef T c_array_type[N];
>     c_array_type & c_array() &;
>     c_array_type && c_array() &&;
>     const c_array_type & c_array() const &;

Add the following subsection to 24.3.7 [array], after [array.data]:

> ##### 23.2.1.5 array::c_array [array.c_array]

>     c_array_type & c_array() &;
>     c_array_type && c_array() &&;
>     const c_array_type & c_array() const &;

> > Returns: `elems`.

Change Zero sized arrays 24.3.7.5 [array.zero]:

> -2- ...

> The type `c_array_type` is unspecified for a zero-sized array.

> -3- The effect of calling `c_array()`, `front()`, or `back()` for a zero-sized array is implementation defined.