Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #70956 > unrolled thread

std::copy with std::ostream_iterator<std::array...>

Started byRalf Goertz <me@myprovider.invalid>
First post2020-01-02 10:37 +0100
Last post2020-01-05 19:55 +0000
Articles 13 — 7 participants

Back to article view | Back to comp.lang.c++


Contents

  std::copy with std::ostream_iterator<std::array...> Ralf Goertz <me@myprovider.invalid> - 2020-01-02 10:37 +0100
    Re: std::copy with std::ostream_iterator<std::array...> Paavo Helde <myfirstname@osa.pri.ee> - 2020-01-02 12:27 +0200
    Re: std::copy with std::ostream_iterator<std::array...> Bo Persson <bo@bo-persson.se> - 2020-01-02 12:26 +0100
      Re: std::copy with std::ostream_iterator<std::array...> Ralf Goertz <me@myprovider.invalid> - 2020-01-02 16:10 +0100
        Re: std::copy with std::ostream_iterator<std::array...> James Kuyper <jameskuyper@alumni.caltech.edu> - 2020-01-02 23:13 -0500
          Re: std::copy with std::ostream_iterator<std::array...> Ralf Goertz <me@myprovider.invalid> - 2020-01-03 07:49 +0100
            Re: std::copy with std::ostream_iterator<std::array...> James Kuyper <jameskuyper@alumni.caltech.edu> - 2020-01-03 08:30 -0500
        Re: std::copy with std::ostream_iterator<std::array...> Bo Persson <bo@bo-persson.se> - 2020-01-03 15:09 +0100
    Re: std::copy with std::ostream_iterator<std::array...> Ike Naar <ike@sdf.lonestar.org> - 2020-01-04 21:46 +0000
      Re: std::copy with std::ostream_iterator<std::array...> Ralf Goertz <me@myprovider.invalid> - 2020-01-05 16:06 +0100
        Re: std::copy with std::ostream_iterator<std::array...> Jorgen Grahn <grahn+nntp@snipabacken.se> - 2020-01-05 18:24 +0000
          Re: std::copy with std::ostream_iterator<std::array...> Öö Tiib <ootiib@hot.ee> - 2020-01-05 10:56 -0800
            Re: std::copy with std::ostream_iterator<std::array...> Jorgen Grahn <grahn+nntp@snipabacken.se> - 2020-01-05 19:55 +0000

#70956 — std::copy with std::ostream_iterator<std::array...>

FromRalf Goertz <me@myprovider.invalid>
Date2020-01-02 10:37 +0100
Subjectstd::copy with std::ostream_iterator<std::array...>
Message-ID<qukdjv$fco$1@dont-email.me>
Hi,

I don't get the problem in the following program. If I use 

using Coords=Foo;

everything is okay but with 

using Coords=std::array<int,2>

I get bombarded with (for me) undecipherable template errors originating
from the std::copy line. Note that without that line it works for both
types, so the operator overload "<<" should not be the problem.

What ist going on

#include <iostream>
#include <iterator>
#include <algorithm>
#include <array>
#include <set>

struct Foo {
    int i,j;
    bool operator<(const struct Foo &r) const { //needed for set
        if (i<r.i) return true;
        return j<r.j;
    }
};

std::ostream & operator<<(std::ostream &o,const std::array<int,2> &c) {
    return o<<'('<<c[0]<<','<<c[1]<<')';
}

std::ostream & operator<<(std::ostream &o,const Foo &c) {
    return o<<'('<<c.i<<','<<c.j<<')';
}
                    
using Coords=Foo;  //this works
//using Coords=std::array<int,2>; //this doesn't

int main() {
    std::set<Coords> ls;
    ls.insert(Coords({47,11}));
    std::cout<<*ls.begin()<<std::endl;
    std::copy(ls.cbegin(),ls.cend(),std::ostream_iterator<Coords>(std::cout," ")); //Compile error with array
    std::cout<<"\n";
}

[toc] | [next] | [standalone]


#70959

FromPaavo Helde <myfirstname@osa.pri.ee>
Date2020-01-02 12:27 +0200
Message-ID<qukgic$589$1@dont-email.me>
In reply to#70956
On 2.01.2020 11:37, Ralf Goertz wrote:
> Hi,
>
> I don't get the problem in the following program. If I use
>
> using Coords=Foo;
>
> everything is okay but with
>
> using Coords=std::array<int,2>
>
> I get bombarded with (for me) undecipherable template errors originating
> from the std::copy line. Note that without that line it works for both
> types, so the operator overload "<<" should not be the problem.

Your example starts to compile if you put the relevant operator<< inside 
namespace std.

[toc] | [prev] | [next] | [standalone]


#70964

FromBo Persson <bo@bo-persson.se>
Date2020-01-02 12:26 +0100
Message-ID<h762c4FgmbdU1@mid.individual.net>
In reply to#70956
On 2020-01-02 at 10:37, Ralf Goertz wrote:
> Hi,
> 
> I don't get the problem in the following program. If I use
> 
> using Coords=Foo;
> 
> everything is okay but with
> 
> using Coords=std::array<int,2>
> 
> I get bombarded with (for me) undecipherable template errors originating
> from the std::copy line. Note that without that line it works for both
> types, so the operator overload "<<" should not be the problem.

But it is.  :-)

Argument dependent lookup will search for an operator in the 
namespace(s) of the arguments (thus its name :-).

If you (or a standard algorithm) use an operator<< with two types from 
namespace std (for example, std::ostream and std::array), the compiler 
will look for an operator<< also in namespace std. There are lots of 
them, but none for std::array.

Your operator is in the global namespace, but std::array is not. So that 
operator will not be found.

It works for Foo, because the type and the operator are in the same 
namespace.

> 
> What ist going on
> 
> #include <iostream>
> #include <iterator>
> #include <algorithm>
> #include <array>
> #include <set>
> 
> struct Foo {
>      int i,j;
>      bool operator<(const struct Foo &r) const { //needed for set
>          if (i<r.i) return true;
>          return j<r.j;
>      }
> };
> 
> std::ostream & operator<<(std::ostream &o,const std::array<int,2> &c) {
>      return o<<'('<<c[0]<<','<<c[1]<<')';
> }
> 
> std::ostream & operator<<(std::ostream &o,const Foo &c) {
>      return o<<'('<<c.i<<','<<c.j<<')';
> }
>                      
> using Coords=Foo;  //this works
> //using Coords=std::array<int,2>; //this doesn't
> 
> int main() {
>      std::set<Coords> ls;
>      ls.insert(Coords({47,11}));
>      std::cout<<*ls.begin()<<std::endl;
>      std::copy(ls.cbegin(),ls.cend(),std::ostream_iterator<Coords>(std::cout," ")); //Compile error with array
>      std::cout<<"\n";
> }
> 

[toc] | [prev] | [next] | [standalone]


#70971

FromRalf Goertz <me@myprovider.invalid>
Date2020-01-02 16:10 +0100
Message-ID<qul14t$msl$1@dont-email.me>
In reply to#70964
Am Thu, 2 Jan 2020 12:26:59 +0100
schrieb Bo Persson <bo@bo-persson.se>:

> On 2020-01-02 at 10:37, Ralf Goertz wrote:
> > Hi,
> > 
> > I don't get the problem in the following program. If I use
> > 
> > using Coords=Foo;
> > 
> > everything is okay but with
> > 
> > using Coords=std::array<int,2>
> > 
> > I get bombarded with (for me) undecipherable template errors
> > originating from the std::copy line. Note that without that line it
> > works for both types, so the operator overload "<<" should not be
> > the problem.  
> 
> But it is.  :-)
> 
> Argument dependent lookup will search for an operator in the 
> namespace(s) of the arguments (thus its name :-).
> 
> If you (or a standard algorithm) use an operator<< with two types
> from namespace std (for example, std::ostream and std::array), the
> compiler will look for an operator<< also in namespace std. There are
> lots of them, but none for std::array.
> 
> Your operator is in the global namespace, but std::array is not. So
> that operator will not be found.
> 
> It works for Foo, because the type and the operator are in the same
> namespace.

Thanks for the explanation. I figured as much after having read Paavo's
reply. But two questions remain. First, I thought it would be illegal
(or at least frowned upon) for user code to inject anything into the std
namespace. And what is the difference for the line

std::cout<<*ls.begin()<<std::endl;

(ls being of type std::set<std::array<int,2>>) although I have defined
the operator in the global namespace and both operands are in std (I
guess). Shouldn't the rule you quoted also lead to a lookup failure?

[toc] | [prev] | [next] | [standalone]


#70995

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2020-01-02 23:13 -0500
Message-ID<qumf12$jee$1@dont-email.me>
In reply to#70971
On 1/2/20 10:10 AM, Ralf Goertz wrote:
...
> reply. But two questions remain. First, I thought it would be illegal
> (or at least frowned upon) for user code to inject anything into the std
> namespace.

"A program may add a template specialization for any standard library
template to namespace std only if the declaration depends on a
user-defined type and the specialization meets the standard library
requirements for the original template and is not explicitly
prohibited." (17.6.4.2.1p1)
"A program may explicitly instantiate a template defined in the standard
library only if the declaration depends on the name of a user-defined
type and the instantiation meets the standard library requirements
for the original template." (17.6.4.2.1p2).

[toc] | [prev] | [next] | [standalone]


#70999

FromRalf Goertz <me@myprovider.invalid>
Date2020-01-03 07:49 +0100
Message-ID<qumo6m$kan$1@dont-email.me>
In reply to#70995
Am Thu, 2 Jan 2020 23:13:22 -0500
schrieb James Kuyper <jameskuyper@alumni.caltech.edu>:

> On 1/2/20 10:10 AM, Ralf Goertz wrote:
> ...
> > reply. But two questions remain. First, I thought it would be
> > illegal (or at least frowned upon) for user code to inject anything
> > into the std namespace.  
> 
> "A program may add a template specialization for any standard library
> template to namespace std only if the declaration depends on a
> user-defined type and the specialization meets the standard library
> requirements for the original template and is not explicitly
> prohibited." (17.6.4.2.1p1)
> "A program may explicitly instantiate a template defined in the
> standard library only if the declaration depends on the name of a
> user-defined type and the instantiation meets the standard library
> requirements for the original template." (17.6.4.2.1p2).

Thanks. Do you also have an answer to the second question:

std::cout<<*ls.begin()<<std::endl;
std::copy(ls.begin(),ls.end(),std::ostream_iterator<Coords>(std::cout," "));

Why doesn't the first of the above lines require the operator << to be
defined in std whereas the second does?

[toc] | [prev] | [next] | [standalone]


#71011

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2020-01-03 08:30 -0500
Message-ID<qunfls$gce$3@dont-email.me>
In reply to#70999
On 1/3/20 1:49 AM, Ralf Goertz wrote:
...
> Thanks. Do you also have an answer to the second question:

No - answering that question would require more time than I can afford
to spend right now. I hope someone else answers it first.

> 
> std::cout<<*ls.begin()<<std::endl;
> std::copy(ls.begin(),ls.end(),std::ostream_iterator<Coords>(std::cout," "));
> 
> Why doesn't the first of the above lines require the operator << to be
> defined in std whereas the second does?
> 

[toc] | [prev] | [next] | [standalone]


#71013

FromBo Persson <bo@bo-persson.se>
Date2020-01-03 15:09 +0100
Message-ID<h79092F4t0jU1@mid.individual.net>
In reply to#70971
On 2020-01-02 at 16:10, Ralf Goertz wrote:
> Am Thu, 2 Jan 2020 12:26:59 +0100
> schrieb Bo Persson <bo@bo-persson.se>:
> 
>> On 2020-01-02 at 10:37, Ralf Goertz wrote:
>>> Hi,
>>>
>>> I don't get the problem in the following program. If I use
>>>
>>> using Coords=Foo;
>>>
>>> everything is okay but with
>>>
>>> using Coords=std::array<int,2>
>>>
>>> I get bombarded with (for me) undecipherable template errors
>>> originating from the std::copy line. Note that without that line it
>>> works for both types, so the operator overload "<<" should not be
>>> the problem.
>>
>> But it is.  :-)
>>
>> Argument dependent lookup will search for an operator in the
>> namespace(s) of the arguments (thus its name :-).
>>
>> If you (or a standard algorithm) use an operator<< with two types
>> from namespace std (for example, std::ostream and std::array), the
>> compiler will look for an operator<< also in namespace std. There are
>> lots of them, but none for std::array.
>>
>> Your operator is in the global namespace, but std::array is not. So
>> that operator will not be found.
>>
>> It works for Foo, because the type and the operator are in the same
>> namespace.
> 
> Thanks for the explanation. I figured as much after having read Paavo's
> reply. But two questions remain. First, I thought it would be illegal
> (or at least frowned upon) for user code to inject anything into the std
> namespace. And what is the difference for the line
> 
> std::cout<<*ls.begin()<<std::endl;
> 
> (ls being of type std::set<std::array<int,2>>) although I have defined
> the operator in the global namespace and both operands are in std (I
> guess). Shouldn't the rule you quoted also lead to a lookup failure?
> 

Yes, but there are more rules (lots of them)...

The compiler also does an Unqualified name lookup

https://en.cppreference.com/w/cpp/language/unqualified_lookup

(about 10 pages of rules) for "<< *ls.begin" and finds the global one 
from main(), because the call is done from the global namespace.


The code for std::copy is inside namespace std so the search for an 
operator starts there. And it finds lots of operator<< in the namespace, 
so it stops there:

"For an unqualified name, that is a name that does not appear to the 
right of a scope resolution operator ::, name lookup examines the scopes 
as described below, until it finds at least one declaration of any kind, 
at which time the lookup stops and no further scopes are examined."


And then the compiler performs an overload resolution (another 30 pages 
in the standard) for the names it has found.

The complete process is *way* too complicated to fully describe in a 
post here, so the recommendation is to always declare the operators in 
the same namespace as (at least one of) the operands, and avoid most of 
the complications.



     Bo Persson

[toc] | [prev] | [next] | [standalone]


#71118

FromIke Naar <ike@sdf.lonestar.org>
Date2020-01-04 21:46 +0000
Message-ID<slrn3flsr121tl.4k8.ike@sdf.lonestar.org>
In reply to#70956
On 2020-01-02, Ralf Goertz <me@myprovider.invalid> wrote:
> struct Foo {
>     int i,j;
>     bool operator<(const struct Foo &r) const { //needed for set
>         if (i<r.i) return true;
>         return j<r.j;

This looks suspect.
Do you want (2,1) to be less than (1,2) ?
if you want to define a lexicographical order on (i,j), the comparison should be

  return i<r.i || (i==r.i && j<r.j);

[toc] | [prev] | [next] | [standalone]


#71146

FromRalf Goertz <me@myprovider.invalid>
Date2020-01-05 16:06 +0100
Message-ID<qusu17$ld9$1@dont-email.me>
In reply to#71118
Am Sat, 4 Jan 2020 21:46:28 -0000 (UTC)
schrieb Ike Naar <ike@sdf.lonestar.org>:

> On 2020-01-02, Ralf Goertz <me@myprovider.invalid> wrote:
> > struct Foo {
> >     int i,j;
> >     bool operator<(const struct Foo &r) const { //needed for set
> >         if (i<r.i) return true;
> >         return j<r.j;  
> 
> This looks suspect.
> Do you want (2,1) to be less than (1,2) ? if you want to define a
> lexicographical order on (i,j), the comparison should be
> 
>   return i<r.i || (i==r.i && j<r.j);

Yeah, I know. I did get it wrong but I only defined the operator in
order to be able to use Foo with the set. I was puzzled by the compile
errors with array. I thought that the fact that array is templated could
be the reason. So I tried the non-templated Foo and the errors were
gone. I would never have guessed that the namespace was the problem.

[toc] | [prev] | [next] | [standalone]


#71150

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2020-01-05 18:24 +0000
Message-ID<slrnr14aeq.9in.grahn+nntp@frailea.sa.invalid>
In reply to#71146
On Sun, 2020-01-05, Ralf Goertz wrote:
> Am Sat, 4 Jan 2020 21:46:28 -0000 (UTC)
> schrieb Ike Naar <ike@sdf.lonestar.org>:
>
>> On 2020-01-02, Ralf Goertz <me@myprovider.invalid> wrote:
>> > struct Foo {
>> >     int i,j;
>> >     bool operator<(const struct Foo &r) const { //needed for set
>> >         if (i<r.i) return true;
>> >         return j<r.j;  
>> 
>> This looks suspect.
>> Do you want (2,1) to be less than (1,2) ? if you want to define a
>> lexicographical order on (i,j), the comparison should be
>> 
>>   return i<r.i || (i==r.i && j<r.j);
>
> Yeah, I know.

I don't know why, but I feel safer defining it like this:

    bool operator<(const struct Foo &r) const {
       if (i==r.i) return j < r.j;
       return i < r.i;
    }

Another option would be std::make_pair, and comparing the pairs.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#71152

FromÖö Tiib <ootiib@hot.ee>
Date2020-01-05 10:56 -0800
Message-ID<2fa235df-fa7d-47f7-b7aa-5bc16f0e2114@googlegroups.com>
In reply to#71150
On Sunday, 5 January 2020 20:24:38 UTC+2, Jorgen Grahn  wrote:
> On Sun, 2020-01-05, Ralf Goertz wrote:
> > Am Sat, 4 Jan 2020 21:46:28 -0000 (UTC)
> > schrieb Ike Naar <ike@sdf.lonestar.org>:
> >
> >> On 2020-01-02, Ralf Goertz <me@myprovider.invalid> wrote:
> >> > struct Foo {
> >> >     int i,j;
> >> >     bool operator<(const struct Foo &r) const { //needed for set
> >> >         if (i<r.i) return true;
> >> >         return j<r.j;  
> >> 
> >> This looks suspect.
> >> Do you want (2,1) to be less than (1,2) ? if you want to define a
> >> lexicographical order on (i,j), the comparison should be
> >> 
> >>   return i<r.i || (i==r.i && j<r.j);
> >
> > Yeah, I know.
> 
> I don't know why, but I feel safer defining it like this:
> 
>     bool operator<(const struct Foo &r) const {
>        if (i==r.i) return j < r.j;
>        return i < r.i;
>     }
> 
> Another option would be std::make_pair, and comparing the pairs.

Funny. I checked that most usual in my code is that pattern:

    return (l.a != r.a) ? l.a < r.a
         : (l.b != r.b) ? l.b < r.b
         : (l.c != r.c) ? l.c < r.c
         : (l.d != r.d) ? l.d < r.d
         : l.e < r.e;

[toc] | [prev] | [next] | [standalone]


#71155

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2020-01-05 19:55 +0000
Message-ID<slrnr14fp6.9in.grahn+nntp@frailea.sa.invalid>
In reply to#71152
On Sun, 2020-01-05, Öö Tiib wrote:
> On Sunday, 5 January 2020 20:24:38 UTC+2, Jorgen Grahn  wrote:
>> On Sun, 2020-01-05, Ralf Goertz wrote:
>> > Am Sat, 4 Jan 2020 21:46:28 -0000 (UTC)
>> > schrieb Ike Naar <ike@sdf.lonestar.org>:
>> >
>> >> On 2020-01-02, Ralf Goertz <me@myprovider.invalid> wrote:
>> >> > struct Foo {
>> >> >     int i,j;
>> >> >     bool operator<(const struct Foo &r) const { //needed for set
>> >> >         if (i<r.i) return true;
>> >> >         return j<r.j;  
>> >> 
>> >> This looks suspect.
>> >> Do you want (2,1) to be less than (1,2) ? if you want to define a
>> >> lexicographical order on (i,j), the comparison should be
>> >> 
>> >>   return i<r.i || (i==r.i && j<r.j);
>> >
>> > Yeah, I know.
>> 
>> I don't know why, but I feel safer defining it like this:
>> 
>>     bool operator<(const struct Foo &r) const {
>>        if (i==r.i) return j < r.j;
>>        return i < r.i;
>>     }
>> 
>> Another option would be std::make_pair, and comparing the pairs.
>
> Funny. I checked that most usual in my code is that pattern:
>
>     return (l.a != r.a) ? l.a < r.a
>          : (l.b != r.b) ? l.b < r.b
>          : (l.c != r.c) ? l.c < r.c
>          : (l.d != r.d) ? l.d < r.d
>          : l.e < r.e;

"The first pair that's different gets to decide." Makes sense!

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web