Skip to content

P2752R3 Static storage for braced initializers #6322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions source/compatibility.tex
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
%!TEX root = std.tex
\infannex{diff}{Compatibility}

\rSec1[diff.cpp23]{\Cpp{} and ISO \CppXXIII{}}

\rSec2[diff.cpp23.general]{General}

\pnum
\indextext{summary!compatibility with ISO \CppXXIII{}}%
Subclause \ref{diff.cpp23} lists the differences between \Cpp{} and
ISO \CppXXIII{} (ISO/IEC 14882:2023, \doccite{Programming Languages --- \Cpp{}}),
by the chapters of this document.

\rSec2[diff.cpp23.expr]{\ref{expr}: expressions}

\diffref{dcl.init.list}
\change
Pointer comparisons between \tcode{initializer_list} objects' backing arrays
are unspecified.
\rationale
Permit the implementation to store backing arrays in static read-only memory.
\effect
Valid \CppXXIII{} code
that relies on the result of pointer comparison between backing arrays
may change behavior.
For example:
\begin{example}
bool ne(std::initializer_list<int> a, std::initializer_list<int> b) {
return a.begin() != b.begin() + 1;
}
bool b = ne({2,3}, {1,2,3}); // unspecified result; previously \tcode{false}
\end{example}

\rSec1[diff.cpp20]{\Cpp{} and ISO \CppXX{}}

\rSec2[diff.cpp20.general]{General}
Expand Down
59 changes: 42 additions & 17 deletions source/declarations.tex
Original file line number Diff line number Diff line change
Expand Up @@ -5991,36 +5991,64 @@
an initializer list as if
the implementation generated and materialized\iref{conv.rval}
a prvalue of type ``array of $N$ \tcode{const E}'',
where $N$ is the number of elements in the
initializer list. Each element of that array is copy-initialized with the
where $N$ is the number of elements in the initializer list;
this is called the initializer list's \defnadj{backing}{array}.
Each element of the backing array is copy-initialized with the
corresponding element of the initializer list, and the
\tcode{std::initializer_list<E>} object is constructed to refer to that array.
\begin{note}
A constructor or conversion function selected for the copy is required to be
accessible\iref{class.access} in the context of the initializer list.
\end{note}
If a narrowing conversion is required to initialize any of the elements, the program is ill-formed.
If a narrowing conversion is required to initialize any of the elements,
the program is ill-formed.
Whether all backing arrays are distinct
(that is, are stored in non-overlapping objects) is unspecified.

\pnum
The backing array has the same lifetime as any other temporary
object\iref{class.temporary}, except that initializing an
\tcode{initializer_list} object from the array extends the lifetime of
the array exactly like binding a reference to a temporary.
\begin{example}
\begin{codeblock}
struct X {
X(std::initializer_list<double> v);
void f(std::initializer_list<double> il);
void g(float x) {
f({1, x, 3});
}
void h() {
f({1, 2, 3});
}

struct A {
mutable int i;
};
X x{ 1,2,3 };
void q(std::initializer_list<A>);
void r() {
q({A{1}, A{2}, A{3}});
}
\end{codeblock}

The initialization will be implemented in a way roughly equivalent to this:
\begin{codeblock}
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
void g(float x) {
const double __a[3] = {double{1}, double{x}, double{3}}; // backing array
f(std::initializer_list<double>(__a, __a+3));
}
void h() {
static constexpr double __b[3] = {double{1}, double{2}, double{3}}; // backing array
f(std::initializer_list<double>(__b, __b+3));
}
void r() {
const A __c[3] = {A{1}, A{2}, A{3}}; // backing array
q(std::initializer_list<A>(__c, __c+3));
}
\end{codeblock}
assuming that the implementation can construct an \tcode{initializer_list} object with a pair of pointers.
assuming that the implementation
can construct an \tcode{initializer_list} object with a pair of pointers, and
with the understanding that \tcode{__b} does not outlive the call to \tcode{f}.
\end{example}

\pnum
The array has the same lifetime as any other temporary
object\iref{class.temporary}, except that initializing an
\tcode{initiali\-zer_list} object from the array extends the lifetime of
the array exactly like binding a reference to a temporary.
\begin{example}
\begin{codeblock}
typedef std::complex<double> cmplx;
Expand All @@ -6047,9 +6075,6 @@
a temporary array to a reference member, so the program is
ill-formed\iref{class.base.init}.
\end{example}
\begin{note}
The implementation is free to allocate the array in read-only memory if an explicit array with the same initializer can be so allocated.
\end{note}

\pnum
A \defnadj{narrowing}{conversion} is an implicit conversion
Expand Down
1 change: 1 addition & 0 deletions source/macros.tex
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@
\newcommand{\CppXIV}{\Cpp{} 2014}
\newcommand{\CppXVII}{\Cpp{} 2017}
\newcommand{\CppXX}{\Cpp{} 2020}
\newcommand{\CppXXIII}{\Cpp{} 2023}
\newcommand{\opt}[1]{#1\ensuremath{_\mathit{\color{black}opt}}}
\newcommand{\bigoh}[1]{\ensuremath{\mathscr{O}(#1)}}

Expand Down