Explicit template arguments to make_pair -- legal (c++11)?


K

K. Frank

Hello Group!

I'm having trouble with "g++ -std=c++0x" when trying to compile

std::make_pair<int, unsigned> (i, u)

where I give explicit template arguments to make_pair, specifically:

#include <utility>
std::pair<int, unsigned> f() {
int i = 1;
unsigned u = 2;
std::pair <int, unsigned> p = std::make_pair<int, unsigned> (i,
u); // <-- legal?
// std::pair <int, unsigned> p = std::make_pair (i, u); // <--
this works
return p;
}

This compiles fine when I just run "g++", but fails when I turn on
experimental support for the new standard, "g++ -std=c++0x",
giving the following error:

C:\>g++ -std=c++0x -c pair_junk2.cpp
pair_junk2.cpp: In function 'std::pair<int, unsigned int> f()':
pair_junk2.cpp:5:68: error: no matching function for call to
'make_pair(int&, unsigned int&)'
pair_junk2.cpp:5:68: note: candidate is:
../lib/gcc/x86_64-w64-mingw32/4.7.0/../../../../include/c++/4.7.0/
bits/stl_pair.h:280:5: note: template<class _T1, class _T2> constexpr
std::pair<typename std::__decay_and_strip<_T1>::__type, typename
std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
../lib/gcc/x86_64-w64-mingw32/4.7.0/../../../../include/c++/4.7.0/
bits/stl_pair.h:280:5: note: template argument deduction/
substitution failed:
pair_junk2.cpp:5:68: note: cannot convert 'i' (type 'int') to
type 'int&&'

I am running a mingw-w64 build of g++: "g++ (GCC) 4.7.0 20110829
(experimental)"

(For what it's worth, the code is accepted by Comeau's online
compiler.)

Is "std::make_pair<int, unsigned> (i, u)" legal in general? Is this
a difference between the old and new standards? Is this a bug
in g++ in std=c++0x mode?

Thanks.


K. Frank
 
Ad

Advertisements

V

Victor Bazarov

[..]
I am running a mingw-w64 build of g++: "g++ (GCC) 4.7.0 20110829
(experimental)"

(For what it's worth, the code is accepted by Comeau's online
compiler.)

Is "std::make_pair<int, unsigned> (i, u)" legal in general? Is this
a difference between the old and new standards? Is this a bug
in g++ in std=c++0x mode?

It was legal before, yes? What I am about 99.8% certain of, is that the
new Standard couldn't have made some relatively recent code (no "auto"
or "register" in odd places) that suddenly illegal. Explicit arguments
to function templates have always been legal and should not present a
problem. I think it must be a bug in the compiler.

V
 
S

SG

I'm having trouble with "g++ -std=c++0x" when trying to compile

   std::make_pair<int, unsigned> (i, u)

where I give explicit template arguments to make_pair
[...]
Is "std::make_pair<int, unsigned> (i, u)" legal in general? Is this
a difference between the old and new standards? Is this a bug
in g++ in std=c++0x mode?

Disclaimer: I havn't checked one of the recent drafts but I'm pretty
sure about what's going on and have a satisfying workaround for you at
the end of this post.

The declaration (probably) changed from

template<class T, class U>
... make_pair(T const& a, U const& b)

to

template<class T, class U>
... make_pair(T && a, U && b)

in C++11. So, the committee extended make_pair by use of the perfect
forwarding technique. This allows the reduction of unnecessary copy
operations with the help of move semantics. If you specify T and/or U
explicitly (and not to be lvalue references), the function will be
expecting rvalue arguments only. Since your arguments i and u are
lvalues and one can't initialize an rvalue reference with an lvalue
(generally), the compiler will reject your code.

However, the purpose of make_pair is actually to *AVOID* having to
specify types manually. We wouldn't need make_pair if you we knew
exactly what kind of pair we are interested in and were willing to
specify its type parameters:

std::pair<int, unsigned> (i, u)

This will do exactly, what you're interested in and is even shorter to
type. So, this change from C++03 to C++11 doesn't really pose a
problem.


Cheers!
SG
 
K

K. Frank

Hi Victor and SG!

Thank you for your comments.

I'm having trouble with "g++ -std=c++0x" when trying to compile
   std::make_pair<int, unsigned> (i, u)
where I give explicit template arguments to make_pair
[...]
Is "std::make_pair<int, unsigned> (i, u)" legal in general?  Is this
a difference between the old and new standards?  Is this a bug
in g++ in std=c++0x mode?

Disclaimer: I havn't checked one of the recent drafts but I'm pretty
sure about what's going on and have a satisfying workaround for you at
the end of this post.

The declaration (probably) changed from

  template<class T, class U>
  ... make_pair(T const& a, U const& b)

to

  template<class T, class U>
  ... make_pair(T && a, U && b)

This makes sense. I don't really understand the details of rvalue
references, but when the compile error occurred, the compiler did
complain:

pair_junk2.cpp:5:68: note: cannot convert 'i' (type 'int') to
type 'int&&'

so your explanation seems reasonable.
in C++11.  So, the committee extended make_pair by use of the perfect
forwarding technique.  This allows the reduction of unnecessary copy
operations with the help of move semantics.  If you specify T and/or U
explicitly (and not to be lvalue references), the function will be
expecting rvalue arguments only.  Since your arguments i and u are
lvalues and one can't initialize an rvalue reference with an lvalue
(generally), the compiler will reject your code.

However, the purpose of make_pair is actually to *AVOID* having to
specify types manually.  We wouldn't need make_pair if you we knew
exactly what kind of pair we are interested in and were willing to
specify its type parameters:

  std::pair<int, unsigned> (i, u)

Yes, this makes sense and works fine. I guess I was so fixated
on make_pair that it didn't occur to me to use pair's constructor
directly. Using the (explicitly templatized) constructor also
probably expresses more directly my intent (that of wanting a
pair of specific types).
This will do exactly, what you're interested in and is even shorter to
type.  So, this change from C++03 to C++11 doesn't really pose a
problem.

From a practical point of view this isn't an issue for me. Both
leaving the types out of make_pair and your suggestion of using
the constructor work fine.

But, coming back to Victor's question, does this represent a bug
in the compiler (or maybe a defect in the new standard)? Generally
the new standards tries to avoid breaking previously legal code,
at least without good reason.

I don't understand rvalue references well enough to have an opinion
about whether this issue is inherent in extended functionality of
the new make_pair, or whether it could be avoided somehow.

Does anyone know if this is a know issue and is accepted as the
necessary price to pay for the new make_pair? (I will say, that,
except for the legacy-code issue, the behavior of the new make_pair
doesn't seem to me to be a problem.)

Also, I've only tried this with "g++ -std=c++0x". Would anyone
know whether other compilers with c++11 support show the same
issue?
Cheers!
SG

Thanks again.


K. Frank
 
S

SG

Hi Victor and SG!
Thank you for your comments.
[...]
SG said:
The declaration (probably) changed from

  template<class T, class U>
  ... make_pair(T const& a, U const& b)

Correction: In C++03 there is no "const&". Well, the standard is not
specific about whether make_pair takes its parameters by value or by
reference. But it specifies its behaviur in terms of pass-by-value
while allowing implementations to save unnecessary copying.
to

  template<class T, class U>
  ... make_pair(T && a, U && b)
[...]

in C++11.  So, the committee extended make_pair by use of the perfect
forwarding technique.  This allows the reduction of unnecessary copy
operations with the help of move semantics.  If you specify T and/or U
explicitly (and not to be lvalue references), the function will be
expecting rvalue arguments only.  Since your arguments i and u are
lvalues and one can't initialize an rvalue reference with an lvalue
(generally), the compiler will reject your code.
[...]
[...]
From a practical point of view this isn't an issue for me.  Both
leaving the types out of make_pair and your suggestion of using
the constructor work fine.

But, coming back to Victor's question, does this represent a bug
in the compiler (or maybe a defect in the new standard)?  Generally
the new standards tries to avoid breaking previously legal code,
at least without good reason.

Compiler bug? No. Every conforming C++11 compiler (or a close
approximation thereof) has to reject the code for the reasons I
mentioned earlier.

Standard defect? In my opinion it's perfectly acceptable. The only
real use case of make_pair is to let the compiler figure out the types
to save typing. That's what it has been designed to do and that's
what still works -- better than ever.
I don't understand rvalue references well enough to have an opinion
about whether this issue is inherent in extended functionality of
the new make_pair, or whether it could be avoided somehow.

No. Obviously, there is a relationship between the template
parameters of make_pair and type parameters of pair for the return
value. But in C++11 this relationship is "far less direct" than one
might expect. There are two things to consider here:

(1) perfect forwarding requires the use of && where the "lvalueness"
of arguments is also encoded in form of template type parameters:
An lvalue argument makes template parameter deduction chose an
lvalue REFERENCE type as type parameter in this case.

(2) we want make_pair to perform certain type transformations:

make_pair param pair param
type T type
-------------------------------------
reference_wrapper<X> X& ("reference unpacking")
const reference_wrapper<X> X& ("reference unpacking")
X (&)[N] X* ("decay copy behaviour")

Perfect forwarding is desirable for efficiency reasons. The type
transformations come in handy when string literals are used as
arguments or you really want to create a pair or tuple with a
reference as member:

int i = 99;
auto p = make_pair(ref(i),"hello");
p.first++;
assert(i==100);

The decltype(p) will be pair said:
Does anyone know if this is a know issue

I don't know.
and is accepted as the
necessary price to pay for the new make_pair?

I don't know.
(I will say, that,
except for the legacy-code issue, the behavior of the new make_pair
doesn't seem to me to be a problem.)

I agree.


Cheers!
SG
 
K

K. Frank

Hello SG!

Thank you for your helpful explanation.

On Jan 17, 7:24 pm, K. Frank wrote:
...

Compiler bug?  No.  Every conforming C++11 compiler (or a close
approximation thereof) has to reject the code for the reasons I
mentioned earlier.

Standard defect?  In my opinion it's perfectly acceptable.  The only
real use case of make_pair is to let the compiler figure out the types
to save typing.  That's what it has been designed to do and that's
what still works -- better than ever.

Makes sense. Thanks.
I don't understand rvalue references well enough to have an opinion
about whether this issue is inherent in extended functionality of
the new make_pair, or whether it could be avoided somehow.

No.  Obviously, there is a relationship between the template
parameters of make_pair and type parameters of pair for the return
value.  But in C++11 this relationship is "far less direct" than one
might expect.  There are two things to consider here:

(1) perfect forwarding requires the use of && where the "lvalueness"
    of arguments is also encoded in form of template type parameters:
    An lvalue argument makes template parameter deduction chose an
    lvalue REFERENCE type as type parameter in this case.

(2) we want make_pair to perform certain type transformations:

       make_pair param            pair param
           type T                    type
       -------------------------------------
       reference_wrapper<X>           X&   ("reference unpacking")
       const reference_wrapper<X>     X&   ("reference unpacking")
       X (&)[N]                       X*   ("decay copy behaviour")

Perfect forwarding is desirable for efficiency reasons.  The type
transformations come in handy when string literals are used as
arguments or you really want to create a pair or tuple with a
reference as member:

  int i = 99;
  auto p = make_pair(ref(i),"hello");
  p.first++;
  assert(i==100);

The decltype(p) will be pair<int&,const char*>.

Again, very helpful.

I guess slowly (but not necessarily surely) I'm learning about
rvalue references and their various uses.
I don't know.

Upon reflection, I bet is was well understood when the change
was proposed.
I don't know.


I agree.

Cheers!
SG


Cheers back at ya!


K. Frank
 
Ad

Advertisements

M

Marc

SG said:
Correction: In C++03 there is no "const&". Well, the standard is not
specific about whether make_pair takes its parameters by value or by
reference. But it specifies its behaviur in terms of pass-by-value
while allowing implementations to save unnecessary copying.

I think that was done for arrays (in C++11 it uses std::decay
instead).
Standard defect? In my opinion it's perfectly acceptable. The only
real use case of make_pair is to let the compiler figure out the types
to save typing. That's what it has been designed to do and that's
what still works -- better than ever.

The one case I can think of is if you want to construct a pair and you
want to specify the first type but let the compiler guess the second.
Not the most frequent use...

I'm pretty sure it is.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top