Section: 23.3.3 [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;
- 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:
std::array::elems
, as well as the note saying that "elems is
shown for exposition only."
std::array::data()
, so that it would
return a reference to the built-in array, instead of a pointer to its
first element.
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 conversionRecommended Open with new wording. Take the required clause and add the explicit conversion operators, not have a
typedef
. At issue still is usedecltype
or useT[N]
. In favour of usingT[N]
, even though use ofdecltype
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 23.3.3.5 [array.zero], which allowed
c_array_type
andc_array()
to be absent for a zero-sized array. Alisdair argued that such wording would disallow certain generic use cases. New wording for 23.3.3.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, 23.3.3 [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 23.3.3 [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 23.3.3.5 [array.zero]:
-2- ...
The type
c_array_type
is unspecified for a zero-sized array.-3- The effect of calling
c_array()
,front()
, orback()
for a zero-sized array is implementation defined.