Topic: Unnecessary special handling for arrays in N2930?


Author: Scott Meyers <usenet@aristeia.com>
Date: Fri, 31 Jul 2009 12:25:50 CST
Raw View
Gabriel Dos Reis wrote:
> Without an actual standardese wording, it is hard to say whether it has
> a fundamental flaw, or whether it is implementable at all, or whether it
> introduces any problem with name lookup.

It's conventional to start by discussing an idea informally, turning to
standardese only when it looks like the idea is highly likely to pan out.
Starting with standardese runs the risk that a good idea can be rejected due to
bad wording.  But since you seem to want to start with a final product, here a
quick "smallest possible delta to the current spec" approach:

- Eliminate this paragraph:

> * If _RangeT is an array type, begin-expr and end-expr are __range and
>        __range + __bound, respectively, where __bound is the array bound. If
>        _RangeT is an array of unknown size or an array of incomplete type, the
>        program is ill-formed.

- To this paragraph, add the text I've indicated in upper case:

> * Otherwise, begin-expr and end-expr are begin(__range)and end(__range),
>        respectively, where begin and end are looked up with argument-dependent
>        lookup ([basic.lookup.argdep]). For the purposes of this name lookup,
>        namespace std is an associated namespace AND THE FOLLOWING DECLARATIONS
           FROM <ITERATOR> ARE ASSUMED TO HAVE BEEN SEEN:

           template<typename T, size_t N> T* begin(T (&array)[N]);
           template<typename T, size_t N> T* end(T (&array)[N]);

It's still not standard-ready, but I hope it's precise enough for technical
evaluation.

Scott


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Mathias Gaunard <loufoque@gmail.com>
Date: Fri, 31 Jul 2009 15:11:25 CST
Raw View
On 30 juil, 19:26, SG <s.gesem...@gmail.com> wrote:

> Side note: Do we need overloads for const? The beauty of the ->decltype
> () syntax is that it automatically picks the correct iterator type.
> Without a const overload we would simply disallow non-const rvalue
> containers. But that should actually be an advantage because an
> iterator from an rvalue container is very likely to be invalid very
> soon.

What if C is not a container but a range adapter?

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Gabriel Dos Reis <gdr@cs.tamu.edu>
Date: Fri, 31 Jul 2009 15:10:42 CST
Raw View
Scott Meyers <usenet@aristeia.com> writes:

| Gabriel Dos Reis wrote:
| > Without an actual standardese wording, it is hard to say whether it has
| > a fundamental flaw, or whether it is implementable at all, or whether it
| > introduces any problem with name lookup.
|
| It's conventional to start by discussing an idea informally, turning to
| standardese only when it looks like the idea is highly likely to pan out.

Yes.  However, the original message was discussing the *fine details*
of a wording.  Therefore, it is  conventional to expect a concrete
alternative.

| Starting with standardese runs the risk that a good idea can be rejected due to
| bad wording.  But since you seem to want to start with a final product, here a
| quick "smallest possible delta to the current spec" approach:
|
| - Eliminate this paragraph:
|
| > * If _RangeT is an array type, begin-expr and end-expr are __range and
| >        __range + __bound, respectively, where __bound is the array bound. If
| >        _RangeT is an array of unknown size or an array of incomplete type, the
| >        program is ill-formed.
|
| - To this paragraph, add the text I've indicated in upper case:
|
| > * Otherwise, begin-expr and end-expr are begin(__range)and end(__range),
| >        respectively, where begin and end are looked up with argument-dependent
| >        lookup ([basic.lookup.argdep]). For the purposes of this name lookup,
| >        namespace std is an associated namespace AND THE FOLLOWING DECLARATIONS
|            FROM <ITERATOR> ARE ASSUMED TO HAVE BEEN SEEN:
|
|            template<typename T, size_t N> T* begin(T (&array)[N]);
|            template<typename T, size_t N> T* end(T (&array)[N]);

"Have been seen" exactly at which point in the program text?  What do you
mean by "have been seen"?  Did you mean as if an "#include <iterator>"
directive was present in the source program?  If yes, exactly where?
Just before the loop?  Just before the enclosing function?  Just before the
enclosing non-local scope?  Just before the outermost non-local scope
definition?  Those are important questions for name lookup and overload
resolution for surrounding statements and expressions.

| It's still not standard-ready, but I hope it's precise enough for technical
| evaluation.

Thanks.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Doug Gregor <doug.gregor@gmail.com>
Date: Mon, 3 Aug 2009 12:13:18 CST
Raw View
On Jul 28, 11:37 pm, Scott Meyers <use...@aristeia.com> wrote:
> N2930 revises the specification for range-based for loops to eliminate
reliance
> on concepts and to address some other issues.  It specifies that a
range-based
> for loop has these semantics:
[snip specification]
> Note that the first bullet essentially gives arrays special handling such
that
> there need not be begin and end function for arrays.  But later N2930
specifies
> that the following functions must exist in <iterator>:
>
>     template<typename T, size_t N> T* begin(T (&array)[N]);
>     template<typename T, size_t N> T* end(T (&array)[N]);
>
> This suggests that arrays need no special handling as long as this header
is
> included.

Right.

> Among the "other issues" addressed by N2930 is making range-based for
loops
> "just work,"  so I'm guessing that the special handling for arrays is
designed
> to handle the case where <iterator> has not been included.  But the result
is
> ugly.

Yes, that was the intent of the special handling for arrays.

Although it wasn't actually discussed, I like having those functions
so that begin/end can become the standard customization points for
iterating through the elements of a container or sequence, just like
swap is the standard customization point for swapping two values.

> Given that the spec already imposes a name-lookup hack regarding begin and
end
> (last sentence of second bullet above), why not just extend that hack to
say
> that the array-versions of begin and end in <iterator> are considered to
be
> available?  It's ugly either way, but at least the way I'm suggesting
there is
> no need for arrays to be given special treatment that the library makes it
look
> like they don't need.

What you suggest requires more cooperation between the language and
library than we've traditionally had, since the compiler would either
need to predefine functions that are described in the library or would
have to coordinate with the library to implicitly include certain
(sub-)headers when needed. We actually did discuss eliminating the
array-specific hack in the Core Working Group, but the compiler/
library dance needed to make the range-based for loop "just work"
ended up being too complicated for our tastes.

Also, the <iterator> header isn't required to be present in a
freestanding implementation, but we still want the range-based for
loop to work for arrays with a freestanding implementation.

 - Doug


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Scott Meyers <usenet@aristeia.com>
Date: Wed, 29 Jul 2009 00:37:51 CST
Raw View
N2930 revises the specification for range-based for loops to eliminate reliance
on concepts and to address some other issues.  It specifies that a range-based
for loop has these semantics:

    The range-based for statement

        for ( for-range-declaration : expression ) statement

     is equivalent to

        {
          auto && __range = ( expression );
          for (auto __begin = begin-expr, __end = end-expr; __begin != __end;
++__begin ) {
            for-range-declaration = *__begin;
            statement
          }
        }

    where __range, __begin, and __end are variables defined for exposition only,
    _RangeT is the type of the expression, and begin-expr and end-expr are
    determined as follows:

        * If _RangeT is an array type, begin-expr and end-expr are __range and
        __range + __bound, respectively, where __bound is the array bound. If
        _RangeT is an array of unknown size or an array of incomplete type, the
        program is ill-formed.

        * Otherwise, begin-expr and end-expr are begin(__range)and end(__range),
        respectively, where begin and end are looked up with argument-dependent
        lookup ([basic.lookup.argdep]). For the purposes of this name lookup,
        namespace std is an associated namespace.

Note that the first bullet essentially gives arrays special handling such that
there need not be begin and end function for arrays.  But later N2930 specifies
that the following functions must exist in <iterator>:

    template<typename T, size_t N> T* begin(T (&array)[N]);
    template<typename T, size_t N> T* end(T (&array)[N]);

This suggests that arrays need no special handling as long as this header is
included.

Among the "other issues" addressed by N2930 is making range-based for loops
"just work,"  so I'm guessing that the special handling for arrays is designed
to handle the case where <iterator> has not been included.  But the result is
ugly.

Given that the spec already imposes a name-lookup hack regarding begin and end
(last sentence of second bullet above), why not just extend that hack to say
that the array-versions of begin and end in <iterator> are considered to be
available?  It's ugly either way, but at least the way I'm suggesting there is
no need for arrays to be given special treatment that the library makes it look
like they don't need.

Scott

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Gabriel Dos Reis <gdr@cs.tamu.edu>
Date: Wed, 29 Jul 2009 12:41:50 CST
Raw View
Scott Meyers <usenet@aristeia.com> writes:

[...]

| Note that the first bullet essentially gives arrays special handling such that
| there need not be begin and end function for arrays.  But later
N2930 specifies
| that the following functions must exist in <iterator>:
|
|     template<typename T, size_t N> T* begin(T (&array)[N]);
|     template<typename T, size_t N> T* end(T (&array)[N]);
|
| This suggests that arrays need no special handling as long as this header is
| included.
|
| Among the "other issues" addressed by N2930 is making range-based for loops
| "just work,"  so I'm guessing that the special handling for arrays is designed
| to handle the case where <iterator> has not been included.  But the result is
| ugly.

On the contrary, the *usage* is simple

  #include <iostream>
  int main() {
     const char* towns = {
       "Paris", "Lyon", "Nice"
     };

     for (s: towns)
        std::cout << s << std::endl;
  }

Nobody needs a header to construct the array `towns'.  Why would you
want people to start including a header if they wanted to iterate
through?  Note that the similarity with:

  #include <iostream>
  #include <vector>
  int main() {
     std::vector<int> v = { 23, 95, 1 };

     for (i: v)
        std::cout << i << std::endl;
  }

does not require <iterator> -- <vector> is included because it is needed
to construct the vector.

I would think that explaining the for-in loop syntax in terms of
<iterator> details would be a mistake -- in terms of teaching
problem-solving with C++0x.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Wed, 29 Jul 2009 12:42:31 CST
Raw View
On 29 Jul., 08:37, Scott Meyers <use...@aristeia.com> wrote:
> N2930 revises the specification for range-based for loops to eliminate reliance
> on concepts and to address some other issues.  It specifies that a range-based
> for loop has these semantics:
>
>     The range-based for statement
>
>         for ( for-range-declaration : expression ) statement
>
>      is equivalent to
>
>         {
>           auto && __range = ( expression );
>           for (auto __begin = begin-expr, __end = end-expr; __begin != __end;
> ++__begin ) {
>             for-range-declaration = *__begin;
>             statement
>           }
>         }
>
>     where __range, __begin, and __end are variables defined for exposition only,
>     _RangeT is the type of the expression, and begin-expr and end-expr are
>     determined as follows:
>
>         * If _RangeT is an array type, begin-expr and end-expr are __range and
>         __range + __bound, respectively, where __bound is the array bound. If
>         _RangeT is an array of unknown size or an array of incomplete type, the
>         program is ill-formed.
>
>         * Otherwise, begin-expr and end-expr are begin(__range)and end(__range),
>         respectively, where begin and end are looked up with argument-dependent
>         lookup ([basic.lookup.argdep]). For the purposes of this name lookup,
>         namespace std is an associated namespace.
>
> Note that the first bullet essentially gives arrays special handling such that
> there need not be begin and end function for arrays.

Correct.

> But later N2930 specifies
> that the following functions must exist in <iterator>:
>
>     template<typename T, size_t N> T* begin(T (&array)[N]);
>     template<typename T, size_t N> T* end(T (&array)[N]);
>
> This suggests that arrays need no special handling as long as this header is
> included.

The reason for adding these function templates to <iterator>
is /not/, that these should be considered for a the new range-
based for-loop.

The above quoted core wording makes it clear that /any/ array
type will be handled by the first bullet, so the second bullet is
/never/ considered for an array.

The whole reason for providing these overloads is to ensure that
any further usage of the new range-idiom in C++ that explicitly
uses free begin and end functions would also work for arrays. In
this case the corresponding user code just needs to add a
"using std::begin; using std::end;" declaration in the scope of
usage, to make them potentially available (in the presence of
<iterator>).

> Among the "other issues" addressed by N2930 is making range-based for loops
> "just work,"  so I'm guessing that the special handling for arrays is designed
> to handle the case where <iterator> has not been included.

This is correct. The new range-based for-loop is a core language
feature and <iterator> is a huge library header, which no-body
wants to have implicitly included if just the new for-loop syntax
is used.

> But the result is ugly.
>
> Given that the spec already imposes a name-lookup hack regarding begin and end
> (last sentence of second bullet above), why not just extend that hack to say
> that the array-versions of begin and end in <iterator> are considered to be
> available?  It's ugly either way, but at least the way I'm suggesting there is
> no need for arrays to be given special treatment that the library makes it look
> like they don't need.

IMO attempting to find consensus whether something is "ugly" or
not is similarly useful as attempting to find consensus about art ;-)

It was considered undesirable to implicitly add library components,
because that would be a novelty with observable consequences.

Defining it the way you suggest would imply that the following code
is valid:

int main() {
 int ia[] = {1, 2, 3};
 for (int i : ia) {}
 std::begin(ia); // well-formed?
 using namespace std; // well-formed?
}

Your suggestion would make sense, if "begin" and "end" were
keywords (No, I don't suggest this). Because this would mean
that we would have a similar situation as for the keyword sizeof
(or typeid) which depends on the library component std::size_t
(or std::type_info). This works:

int main() {
 int ia[] = {1, 2, 3};
 sizeof(ia);
}

without <cstddef> in sight. But using sizeof does not
introduce the types ::size_t or std::size_t:

int main() {
 int ia[] = {1, 2, 3};
 std::size_t sz = sizeof(ia); // Error
}

At this point I see no reason what advantage would exist,
if the range-based for-loop rules would try to look like a single
uniform rule for native arrays compared to other types?!

Let me add one comment for the new for the special lookup
rule in the for-loop: This "hack" was added to ensure that the
following "just works":

#include <iterator>

class Cont {
 ...
public:
 int* begin() { return ...; }
 int* end() { return ...; }
};

int main() {
 Cont c = ...;
 for (int i : c) {}
}

With usual look-up rules the newly suggested overloads

namespace std {
 template<typename C> auto begin(C& c) -> decltype(c.begin());
 template<typename C> auto begin(const C& c) -> decltype(c.begin());
 template<typename C> auto end(C& c) -> decltype(c.end());
 template<typename C> auto end(const C& c) -> decltype(c.end());
}

in <iterator> would not be considered, because Cont does not have
namespace std as associated namespace according to usual ADL
rules. An alternative definition of the for-loop rule could have been
written like so

   {
     auto && __range = ( expression );
     using std::begin;
     using std::end;
     for (auto __begin = begin-expr, __end = end-expr; __begin !=
__end; ++__begin ) {
       for-range-declaration = *__begin;
       statement
     }
   }

but this would be ill-formed, if namespace std hadn't been declared
before. If we try to fix this by changing the definition to

namespace std {}
...
   {
     auto && __range = ( expression );
     using std::begin;
     using std::end;
     for (auto __begin = begin-expr, __end = end-expr; __begin !=
__end; ++__begin ) {
       for-range-declaration = *__begin;
       statement
     }
   }

this would have the effect again, that the presence of the for-loop
would silently introduce the declaration of namespace std, which
has a user-testable effect.

HTH & Greetings from Bremen,

Daniel Kr   gler


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Scott Meyers <usenet@aristeia.com>
Date: Wed, 29 Jul 2009 16:16:45 CST
Raw View
Gabriel Dos Reis wrote:
> On the contrary, the *usage* is simple

I have no complaint about how programmers use the features, just about how the
standard specifies it.  It hacks the name lookup rules to make std::begin and
std::end visible for ADL on non-array types, turns arrays into a special case to
avoid the use of std::begin and std::end on them, mandates the existence of
std::begin and std::end for arrays in a particular header, then uses the
mandatory placement of these functions in the header to justify the special
handling for arrays that could just as easily be handled by further hacking the
already-hacked name lookup rules.  Whether this would be an improvement is
debatable, but it would certainly simplify the specification of range-based fors.

Scott


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Scott Meyers <usenet@aristeia.com>
Date: Wed, 29 Jul 2009 17:02:51 CST
Raw View
Daniel Kr   gler wrote:
> It was considered undesirable to implicitly add library components,
> because that would be a novelty with observable consequences.

I didn't suggest adding library components, I suggested further hacking the
already-hacked name lookup rules as specified here:

> * Otherwise, begin-expr and end-expr are begin(__range)and end(__range),
>        respectively, where begin and end are looked up with argument-dependent
>        lookup ([basic.lookup.argdep]). For the purposes of this name lookup,
>        namespace std is an associated namespace.

> Defining it the way you suggest would imply that the following code
> is valid:
>
> int main() {
>  int ia[] = {1, 2, 3};
>  for (int i : ia) {}
>  std::begin(ia); // well-formed?
>  using namespace std; // well-formed?
> }

Neither is well-formed.  The name-lookup hack would apply only to the
range-based for, just like it does now for UDTs.

Scott


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Gabriel Dos Reis <gdr@cs.tamu.edu>
Date: Wed, 29 Jul 2009 18:07:29 CST
Raw View
Scott Meyers <usenet@aristeia.com> writes:

| Gabriel Dos Reis wrote:
| > On the contrary, the *usage* is simple
|
| I have no complaint about how programmers use the features, just about
| how the standard specifies it.

Do you have an alternative formulation that achieves the same effect as
far as the usage goes?


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: SG <s.gesemann@gmail.com>
Date: Thu, 30 Jul 2009 11:26:28 CST
Raw View
On 29 Jul., 20:42, Daniel Kr   gler <daniel.krueg...@googlemail.com>
wrote:
>
> With usual look-up rules the newly suggested overloads
>
> namespace std {
>  template<typename C> auto begin(C& c) -> decltype(c.begin());
>  template<typename C> auto begin(const C& c) -> decltype(c.begin());
>  template<typename C> auto end(C& c) -> decltype(c.end());
>  template<typename C> auto end(const C& c) -> decltype(c.end());
> }

Side note: Do we need overloads for const? The beauty of the ->decltype
() syntax is that it automatically picks the correct iterator type.
Without a const overload we would simply disallow non-const rvalue
containers. But that should actually be an advantage because an
iterator from an rvalue container is very likely to be invalid very
soon. Since the for-in loop contains a

 auto && __range = /range-expression/ ;

and __range is handled as an lvalue afterwards, this should not be a
problem.

Cheers!
SG


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Scott Meyers <usenet@aristeia.com>
Date: Thu, 30 Jul 2009 11:29:12 CST
Raw View
Gabriel Dos Reis wrote:
> Do you have an alternative formulation that achieves the same effect as
> far as the usage goes?

 From my original post:

> Given that the spec already imposes a name-lookup hack regarding begin and end
> (last sentence of second bullet above), why not just extend that hack to say
> that the array-versions of begin and end in <iterator> are considered to be
> available?

That's not standardese, but it's the basic idea.  Is there a
fundamental flaw in
the approach?  If not, coming up with hack extension wording would be fairly
straightforward, I believe.

Scott

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Gabriel Dos Reis <gdr@cs.tamu.edu>
Date: Thu, 30 Jul 2009 15:01:46 CST
Raw View
Scott Meyers <usenet@aristeia.com> writes:

| Gabriel Dos Reis wrote:
| > Do you have an alternative formulation that achieves the same effect as
| > far as the usage goes?
|
|  From my original post:
|
| > Given that the spec already imposes a name-lookup hack regarding begin and end
| > (last sentence of second bullet above), why not just extend that hack to say
| > that the array-versions of begin and end in <iterator> are considered to be
| > available?
|
| That's not standardese, but it's the basic idea.

Sorry if I did not take that as an alternative formulation.  As you know,
the devil is in the details.  Since, you were discusing a well-defined
standard wording, I took it for granted that an actual alternative
formulation will come in standardese so that everybody could judge
    (1) whether it covers the same usage as the existing one
    (2) it is a far better specification
    (3) it does not unwillingly introduce any bug
    (4) it is implementable.

| Is there a fundamental flaw in  the approach?

Without an actual standardese wording, it is hard to say whether it has
a fundamental flaw, or whether it is implementable at all, or whether it
introduces any problem with name lookup.


| If not, coming up with hack extension wording would be fairly
| straightforward, I believe.

I believe producing an alternative standardese wording would be the only
way to judge.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]