From: Rodolfo Lima on
Hi, as seen in the last C++0x standard draft, initializer_list's
begin/
end/size aren't constexpr anymore. What is the rationale for that? The
returned values are known at compile time, I don't see why they cannot
be constexpr.

Regards,
Rodolfo Lima

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Daniel Krügler on
On 29 Jun., 22:24, Rodolfo Lima <rodo...(a)rodsoft.org> wrote:
> Hi, as seen in the last C++0x standard draft, initializer_list's
> begin/end/size aren't constexpr anymore. What is the rationale for
> that? The returned values are known at compile time, I don't see
> why they cannot be constexpr.

First, "anymore" implies that at some point of time the
standard draft did contain these functions with a
constexpr specifier - but this never had been the case,
because the first accepted proposal paper

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm

added to

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2691.pdf

did not suggest them. Nevertheless, it is correct, that the
first initializer list papers (e.g. n1919, n2100, ...)
did consider such a specification, but those papers also
mentioned some problems with this idea. They came to
the conclusion that if we consider std::initializer_list
objects as intrinsic types this could somehow be realized
via a special rule solely valid for this intrinsic.

As of the current definition of constexpr functions
and as of the current specification of the initialization
of a std::initializer_list object, this could *not*
be handled by the "normal" language rules. Additionally
it would lead to some severe constraints when the
functions could be used as constexpr functions. To
explain why, lets consider a possible std::initializer_list
implementation that attempts to do the best to realize
that. The definition we could try could be as
follows (ignoring typedefs, includes, etc.):

namespace std {
template<class E> class initializer_list {
const E* beg;
size_t sz;
public:
constexpr initializer_list(const E* beg, size_t sz) : beg(beg),
sz(sz) {}
constexpr size_t size() { return sz; }
[..]
};
}

Now this would make the following program well-formed:

#include <initializer_list>

constexpr std::initializer_list<int> li = {0, 42};
constexpr int sz = li.size();

int main() {}

but this slightly modified program wouldn't be:

#include <initializer_list>

int main() {
constexpr std::initializer_list<int> li = {0, 42};
constexpr int sz = li.size();
}

The reason is, that in the second example the current
specification of the initialization of a std::initializer_list
object (see 8.5.4 [dcl.init.list]/4 in the FCD) meets a
constraint of constant expressions (see 5.19 [expr.const]/2):

"A conditional-expression is a constant expression unless
it involves one of the following as a potentially evaluated
subexpression [..]
� an array-to-pointer conversion (4.2) that is applied to
a glvalue that does not designate an object with static
storage duration; [..]
"

This constraint is not satisfied for the second example
and thus the initialization of the local li object
would be ill-formed (even though the size of that
array could still be evaluated as a constant expression).

This means that by normal FCD language rules, users
could only benefit from constexpr functions of
std::initializer_list, if this is an object of static
storage duration - I consider this as a very rare
case.

As indicated above, it would still be possible to
get away language restrictions for an compiler-
intrinsic type as std::initializer_list and just
*require* that size() is supposed to be a constexpr
function. This is probably possible, but at this
point in time

a) it would enforce *all* implementations to realize
this (This is not as simple as it looks - even the
decimal type specification as of

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2849.pdf

does not require decimals to be represented as
compiler-intrinsic types).

b) it is currently unknown whether the effort
required for this specification is justified.
It is probably wiser to wait and to see how
implementations can handle this over the first
time and to possibly consider a stronger
requirement in the future.

HTH & Greetings from Bremen,

Daniel Kr�gler


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Rodolfo Lima on
Hi Daniel, first of all, thanks for your throughout explanation.

On 1 jul, 11:49, Daniel Kr�gler <daniel.krueg...(a)googlemail.com> >
First, "anymore" implies that at some point of time the
> standard draft did contain these functions with a
> constexpr specifier - but this never had been the case,
> because the first accepted proposal paper

Sure, my bad.

> As of the current definition of constexpr functions
> and as of the current specification of the initialization
> of a std::initializer_list object, this could *not*
> be handled by the "normal" language rules. Additionally
> it would lead to some severe constraints when the
> functions could be used as constexpr functions. To

I'm aware of the difficulties involved, but didn't know about the
constraints you've mentioned. They would indeed make constexpr
initializer_list<E>::size next to useless.

I wonder (maybe na�vely), if initializer_list could be something like
this:

template <class E, size_t N=numeric_limits<size_t>::max()>
class initializer_list
{
const E *m_array;
public:
initializer_list(const E *a) : m_array(a) {}

const E *begin() const { return m_array; }
const E *end() const { return begin() + size(); }
constexpr size_t size() const { return N; }
};

template <class E>
class initializer_list<E, numeric_limits<size_t>::max()>
{
const E *m_array;
size_t m_size;
public:
initializer_list(const E *a, size_t s)
: m_array(a), m_size(s) {}

const E *begin() const { return m_array; }
const E *end() const { return begin() + size(); }
size_t size() const { return m_size; }
};

So, whenever the compiler must convert a list into a
initializer_list<E>, it would do as it currently does, according to
FCD. But, to convert to a initializer_list<E,N>, the list must have
exactly N items (or if N is a template argument to be deduced, N would
be deduced to the number of list items).

An valid use case is the following: suppose I'm creating a vector/
matrix class whose dimensions are known at compile time. So I would
have (except for any typos, includes, ...):

//----------------------------------------

using namespace std;

template <class T, int N>
class Vector
{
public:
Vector(T v)
{
fill(m_data, m_data+N, v);
}
Vector(const initializer_list<T,N> &cols)
{
copy(cols.begin(), cols.end(), m_data);
}

T &operator[](int j) { return m_data[j]; }
const T &operator[](int j) const { return m_data[j]; }
private:
T m_data[N];
};

template <class T, int M, int N>
class Matrix
{
public:
Matrix(T v)
{
for(int i=0; i<M; ++i)
for(int j=0; j<N; ++j)
m_data[i][j] = i==j ? v : 0;
}
Matrix(const initializer_list<Vector<T,N>, M> &rows)
{
copy(rows.begin(), rows.end(), m_data);
}

Vector<T,N> &operator[](int i) { return m_data[i]; }
const Vector<T,N> &operator[](int i) const { return m_data[i]; }

private:
Vector<T,N> m_data[M];
};

template <class T, int M, int N>
auto create_matrix(const std::initializer_list<Vector<T,N>,M> &rows)
-> Matrix<T,M,N>
{
return Matrix<T,M,N>(rows);
}

Matrix<int,2,2> m1{{1,2},{3,4}};
auto m2 = create_matrix({1,2},{3,4});

assert(m1 == m2); // supposing there is a Matrix::operator==

//----------------------------------------

For vectors I can create a variadic template constructor and
statically assert its size to be equal to N, the call to Vector<T,
1>{1} would end up calling the "wrong" constructor (the first), but
with the same semantics of the second.

But I can't do the same with Matrix, since the variadic parameters
would be deduced to be std::initializer_list, which is invalid
according to FCD's 14.9.2.5 [temp.deduct.type]/5 paragraph, so I have
to resort to runtime size checking, which is sad.

Regards,
Rodolfo Lima.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Daniel Krügler on
On 2 Jul., 23:09, Rodolfo Lima <rodo...(a)rodsoft.org> wrote:
> I wonder (maybe na vely), if initializer_list could be something like
> this:
>
> template <class E, size_t N=numeric_limits<size_t>::max()>
> class initializer_list
> {
> const E *m_array;
> public:
> initializer_list(const E *a) : m_array(a) {}
>
> const E *begin() const { return m_array; }
> const E *end() const { return begin() + size(); }
> constexpr size_t size() const { return N; }
> };
> So, whenever the compiler must convert a list into a
> initializer_list<E>, it would do as it currently does, according to
> FCD. But, to convert to a initializer_list<E,N>, the list must have
> exactly N items (or if N is a template argument to be deduced, N would
> be deduced to the number of list items).

<nod>, this would indeed work, but it was one of the
main motivations to make initializer_list *only*
dependent on the type, not on the size. This was considered
important to prevent different overloads for different sizes.
In fact, there would otherwise not much of a win compared
to std::array or overloads that accepts references to
arrays as in

template<class T, size_t N> void f(T (&)[N]);

> An valid use case is the following: suppose I'm creating a vector/
> matrix class whose dimensions are known at compile time. So I would
> have (except for any typos, includes, ...):

[..]

> For vectors I can create a variadic template constructor and
> statically assert its size to be equal to N, the call to Vector<T,
> 1>{1} would end up calling the "wrong" constructor (the first), but
> with the same semantics of the second.

As of the current wording, no constructor different from an
initializer_list constructor will be considered for
Vector<T, 1>{1}. Hopefully an NB comment has been submitted
to fix that problem.

> But I can't do the same with Matrix, since the variadic parameters
> would be deduced to be std::initializer_list, which is invalid
> according to FCD's 14.9.2.5 [temp.deduct.type]/5 paragraph, so I have
> to resort to runtime size checking, which is sad.

I agree that we really want to realize constexpr
size() function for initializer_list. Maybe a
special rule for initializer_list is adequate, the
question is when.

Greetings from Bremen,

Daniel Kr�gler


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Rodolfo Lima on
On 2 jul, 21:33, Daniel Kr�gler <daniel.krueg...(a)googlemail.com>
wrote:
> <nod>, this would indeed work, but it was one of the
> main motivations to make initializer_list *only*
> dependent on the type, not on the size. This was considered
> important to prevent different overloads for different sizes.
> In fact, there would otherwise not much of a win compared
> to std::array or overloads that accepts references to
> arrays as in

In my proposal, in cases where the list's size isn't needed at compile
time, the partial specialization would be used, making
initializer_list not dependent on list's size (would be the case for
std::vector, for instance). Only when I *want* different
instantiations for different sizes the base template (dependent on
list's size) would be used. The way I see it is a win-win situation,
isn't it?

Regards,
rod


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]