Topic: Problem with std::make_pair?


Author: Gabriel Dos_Reis <gdosreis@korrigan.inria.fr>
Date: 1999/08/04
Raw View
ark@research.att.com (Andrew Koenig) writes:

[...]

|
| I came into the middle of this discussion, which may help explain
| why I don't understand the problem.

so am I :-)

|
| make_pair("Joe", 134567) presumably yields an object of type
| pair<const char*, int>  (though many implementations will drop
| the const, which should be immaterial).  phonebook.insert wants
| a pair<const string, long>, which can be constructed from a
| pair<const char*, int>.  So I see no reason why this example
| shouldn't work.
|
| What am I missing?

Argument dedcution.

In
 make_pair("Joe", 134567);

"Joe" is of type const char[4] and argument deduction rules don't let
it decay to const char*.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: zdv176@zam229.zam.kfa-juelich.de (B.Mohr)
Date: 1999/08/02
Raw View
Valentin Bonnard <Bonnard.V@wanadoo.fr> writes:
> But the problem here is _not_ the assignement to p,
> as you seem to believe. It's just the
> std::make_pair("B", 2) call in itself which doesn't
> compile.
> The real problem is with the fact that gcc tries to
> assign _arrays_, and thus generates the error:
> incompatible types in assignment of `const char[2]' to `char[2]'

I understand this. But why did it work with g++/egcs before 2.95
and KCC before 3.4? I am sure KCC never allowed array assigment.

> So your code is ill-formed, egcs is correct here.
> (And it took me about half an hour to figure out.)

I believe you but there are dozens of books out there (e.g. Matt Austerns
great book on "Generic Programming and the STL"(?)) with the following
example (out of my head):

  #include <iostream>
  #include <map>
  #include <string>

  int main() {
    std::multimap<std::string,long> phonebook;
    phonebook.insert(std::make_pair("Joe", 134567));         /*1*/
    //...
  }

They all worked fine but if your analysis is correct they are wrong
because line /*1*/ no longer compiles. I used this example in my STL
course; I am not sure whether I can explain my beginner programmers
why this example doesn't work.

Thanks
Bernd
--
Bernd Mohr / Research Centre Juelich, ZAM, Germany / B.Mohr@fz-juelich.de


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: zdv176@zam229.zam.kfa-juelich.de (B.Mohr)
Date: 1999/08/02
Raw View
Valentin Bonnard <Bonnard.V@wanadoo.fr> writes:
> While std::make_pair<const char*, int> ("A", 1)
> would work, std::make_pair ("A", 1) doesn't.
>
> So your code is ill-formed, egcs is correct here.

Sorry, forgot something to ask in my last follow-up:

As explained in my other answers to this thread, I still think
make_pair should be able to handle string literal -> std::string
"conversion". So, is it possible to rewrite pair<> or make_pair<>
in a standard conforming way that my code would compile?

Bernd
--
Bernd Mohr / Research Centre Juelich, ZAM, Germany / B.Mohr@fz-juelich.de
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: 1999/08/02
Raw View
In article <zdv176.933510565@zam229>, B.Mohr <zdv176@zam229.zam.kfa-
juelich.de> writes
>  int main() {
>    std::pair<std::string, int> p("A", 1);        /*1*/
>    p = std::make_pair("B", 2);                   /*2*/
>    p = std::make_pair(std::string("B"), 2);      /*3*/
>  }
I am trying to work out why the fact that string literals are of type
array of const char has anything to do with this.  You have defined p to
be of type pair<std::string, int>  then you make a pair of type <(const
char)[2], int> and expect them to be assignable.  Why should they be,
with or without the const qualification?

Francis Glassborow      Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: zdv176@zam229.zam.kfa-juelich.de (B.Mohr)
Date: 1999/08/02
Raw View
sbnaran@localhost.localdomain (Siemel Naran) writes:
>>    std::pair<std::string, int> p("A", 1);        /*1*/
>>    p = std::make_pair("B", 2);                   /*2*/

>If your compiler allows array assignment (and KCC and EDG compilers do
>not), then the above code compiles but it is inefficient.

I understand this. But why did it work with g++/egcs before 2.95
and KCC before 3.4? I am sure KCC never allowed array assigment.

>How about this:
>     typedef std::pair<std::string, int> Pair;
>     Pair p("A",1);
>     p=Pair("B",2);

I know there are all kind of work-arounds (using char* instead of string,
your soultion above and dozens others). But I still think my
line above marked /*1*/ is the most "natural" (something which
you find in all books / websites about STL and therefore think first).
Also, make_pair works this way with almost every type, but it looks like
not with std::string).

Bernd
--
Bernd Mohr / Research Centre Juelich, ZAM, Germany / B.Mohr@fz-juelich.de
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: ark@research.att.com (Andrew Koenig)
Date: 1999/08/02
Raw View
In article <zdv176.933594420@zam229>,
B.Mohr <zdv176@zam229.zam.kfa-juelich.de> wrote:

>I believe you but there are dozens of books out there (e.g. Matt Austerns
>great book on "Generic Programming and the STL"(?)) with the following
>example (out of my head):

>  #include <iostream>
>  #include <map>
>  #include <string>

>  int main() {
>    std::multimap<std::string,long> phonebook;
>    phonebook.insert(std::make_pair("Joe", 134567));         /*1*/
>    //...
>  }

>They all worked fine but if your analysis is correct they are wrong
>because line /*1*/ no longer compiles. I used this example in my STL
>course; I am not sure whether I can explain my beginner programmers
>why this example doesn't work.

I came into the middle of this discussion, which may help explain
why I don't understand the problem.

make_pair("Joe", 134567) presumably yields an object of type
pair<const char*, int>  (though many implementations will drop
the const, which should be immaterial).  phonebook.insert wants
a pair<const string, long>, which can be constructed from a
pair<const char*, int>.  So I see no reason why this example
shouldn't work.

What am I missing?
--
Andrew Koenig, ark@research.att.com, http://www.research.att.com/info/ark



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/08/03
Raw View
On 2 Aug 1999 20:00:04 GMT, Andrew Koenig <ark@research.att.com> wrote:

>make_pair("Joe", 134567) presumably yields an object of type
>pair<const char*, int>  (though many implementations will drop
>the const, which should be immaterial).  phonebook.insert wants
>a pair<const string, long>, which can be constructed from a
>pair<const char*, int>.  So I see no reason why this example
>shouldn't work.

No.  The make_pair yields an object of type std::pair<const char [4],int>.
This is because of the combination of pass by reference and template
argument deduction.
   template <class T1, class T2>
   pair<T1,T2> make_pair(const T1&, const T2&);
So T1 is 'const char[4]' and T2 is 'int'.  That is, the implicit
instantiation is
   pair<const char[4],int> make_pair(const char (&)[4], const int&);

I still want to know why g++ uses
   pair<char[4],int>
in its error messags.

Had the declaration of make_pair been
   template <class T1, class T2>
   pair<T1,T2> make_pair(T1, T2);
we would have
   pair<const char[4],int> make_pair(const char [4], int);
As C++ does not let you pass arrays by value, we have the phenomenon
of array decay, and the result is:
   pair<const char *,int> make_pair(const char *, int);

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/04
Raw View
B.Mohr wrote:

> Valentin Bonnard <Bonnard.V@wanadoo.fr> writes:

> > So your code is ill-formed, egcs is correct here.
>
> Sorry, forgot something to ask in my last follow-up:
>
> As explained in my other answers to this thread, I still think
> make_pair should be able to handle string literal -> std::string
> "conversion".

Again, there is no problem with the conversion in itself.

> So, is it possible to rewrite pair<> or make_pair<>
> in a standard conforming way that my code would compile?

Use non reference arguments, it will force the char array to
decay:

#include <utility>
#include <string>

template <typename T1, typename T2>
std::pair<T1, T2> my_make_pair (T1 l, T2 r)
{
    return std::pair<T1, T2> (l, r);
}

std::pair<std::string, int> p = my_make_pair ("A", 1);

The cost is an additionnal copy of both arguments.

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/04
Raw View
Siemel Naran wrote:
>
> On 01 Aug 99 20:34:26 GMT, Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:

> >But the problem here is _not_ the assignement to p,
> >as you seem to believe. It's just the
> >std::make_pair("B", 2) call in itself which doesn't
> >compile.
> >
> >The real problem is with the fact that gcc tries to
> >assign _arrays_, and thus generates the error:
> >
> >incompatible types in assignment of `const char[2]' to `char[2]'
>
> But why this error?

> I believe that template argument deduction must deduce this
>    T1==const char[2] // char[0]=='B', char[1]==0
>    T2==int

Almost

> So why then does egcs issue an error message like this:
>    /usr/include/g++/stl_pair.h: In method
>    `pair<char[2],int>::pair<char[2], int>
>       (const char (&)[2], const int &)':
> It should say
>    /usr/include/g++/stl_pair.h: In method
>    `pair<const char[2],int>::pair<const char[2], int>
>       (const char (&)[2], const int &)':

No. Again, the declaration of make_pair is:

  template <class T1, class T2>
  pair<T1,T2> make_pair(___CONST___ T1&, const T2&);

> Now if you compile without "--pedantic", then the compiler allows
> array assignment

That's correct

> and the code should compile.

Maybe

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: zdv176@zam229.zam.kfa-juelich.de (B.Mohr)
Date: 1999/08/01
Raw View
Hi all,
after upgrading to the latest EDG based compilers (e.g. KCC 3.4)
or gcc-2.95 aka egcs-1.2 the following little program does not compile
anymore:

  #include <iostream>
  #include <string>
  #include <utility>

  int main() {
    std::pair<std::string, int> p("A", 1);        /*1*/
    p = std::make_pair("B", 2);                   /*2*/
    p = std::make_pair(std::string("B"), 2);      /*3*/
  }

The line marked /*2*/ generates the error

  incompatible types in assignment of `const char[2]' to `char[2]'

somewhere in <utility>. I was told this is because these compilers now
treat string constants as type `const char[n]', rather than `char[n]' as
requested by the standard. So which of the following is true:

a) the compilers are right. That would be very bad from my point of view
   because the line above is used in dozens of STL books in examples
   for multimap (e.g. multimap<string,int> as type for a phone book)
   and would make std::make_pair useless for strings (if I have to type
   something like /*3*/ to get it work)

b) the compilers are wrong - the line should compile. Something is
   wrong with the definition of std::make_pair or std::pair in the STL
   of these compilers.

Thanks for your time
Bernd
--
Bernd Mohr / Research Centre Juelich, ZAM, Germany / B.Mohr@fz-juelich.de
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/08/01
Raw View
B.Mohr wrote:

>   #include <iostream>
>   #include <string>
>   #include <utility>
>
>   int main() {
>     std::pair<std::string, int> p("A", 1);        /*1*/
>     p = std::make_pair("B", 2);                   /*2*/
>     p = std::make_pair(std::string("B"), 2);      /*3*/
>   }

1) of course std::make_pair("B", 2) doesn't have type
   std::pair<std::string, int>

2) pair defines a converting constructor

> I was told this is because these compilers now
> treat string constants as type `const char[n]', rather than `char[n]' as
> requested by the standard.

Correct treatement for string litteral. But it doesn't
change the fact that a conversion is needed in /*2*/.

And of course there is a conversion from const char[]
to string as there is one for char[] to string. The
former is even better.

But the problem here is _not_ the assignement to p,
as you seem to believe. It's just the
std::make_pair("B", 2) call in itself which doesn't
compile.

The real problem is with the fact that gcc tries to
assign _arrays_, and thus generates the error:

incompatible types in assignment of `const char[2]' to `char[2]'

Note the make_pair definition:

  template <class T1, class T2>
  pair<T1,T2> make_pair(const T1&, const T2&);

Here the arguments are references, so the array "A"
doesn't decay to a pointer of type const char*.

While std::make_pair<const char*, int> ("A", 1)
would work, std::make_pair ("A", 1) doesn't.

The problem is as with:

struct T { char c[2]; T (char const (&r)[2]) : c (r) {} };

So your code is ill-formed, egcs is correct here.

(And it took me about half an hour to figure out.)

--

Valentin Bonnard
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/08/02
Raw View
On 01 Aug 99 20:34:26 GMT, Valentin Bonnard <Bonnard.V@wanadoo.fr> wrote:
>B.Mohr wrote:

>>     std::pair<std::string, int> p("A", 1);        /*1*/
>>     p = std::make_pair("B", 2);                   /*2*/


>But the problem here is _not_ the assignement to p,
>as you seem to believe. It's just the
>std::make_pair("B", 2) call in itself which doesn't
>compile.
>
>The real problem is with the fact that gcc tries to
>assign _arrays_, and thus generates the error:
>
>incompatible types in assignment of `const char[2]' to `char[2]'

But why this error?

>Note the make_pair definition:
>
>  template <class T1, class T2>
>  pair<T1,T2> make_pair(const T1&, const T2&);
>
>Here the arguments are references, so the array "A"
>doesn't decay to a pointer of type const char*.

I believe that template argument deduction must deduce this
   T1==const char[2] // char[0]=='B', char[1]==0
   T2==int
So why then does egcs issue an error message like this:
   /usr/include/g++/stl_pair.h: In method
   `pair<char[2],int>::pair<char[2], int>
      (const char (&)[2], const int &)':
It should say
   /usr/include/g++/stl_pair.h: In method
   `pair<const char[2],int>::pair<const char[2], int>
      (const char (&)[2], const int &)':


Now if you compile without "--pedantic", then the compiler allows
array assignment and the code should compile.

And if you compile with "--pedantic", then the code should not
compile on the grounds that array assignment is forbidden.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/08/02
Raw View
On 01 Aug 99 19:22:27 GMT, B.Mohr <zdv176@zam229.zam.kfa-juelich.de> wrote:

>    std::pair<std::string, int> p("A", 1);        /*1*/
>    p = std::make_pair("B", 2);                   /*2*/

If your compiler allows array assignment (and KCC and EDG compilers do
not), then the above code compiles but it is inefficient.  The
"std::make_pair(...)" makes a whole new array of chars -- in this case
only 2 chars, with char[0] as 'B' and char[1] as null.  Then it copies
this temporary into 'p' and finally destroys the temporary.

How about this:
     typedef std::pair<std::string, int> Pair;
     Pair p("A",1);
     p=Pair("B",2);

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]