Explicit template arguments in std::make_pair

Hi,

This simple program fails to compile with -std=c++11, but compiles ok with c++03. This seems consistent between libc++/libstdc++. Is this due to a change in the standard? What is the reason behind it?

#include <utility>

std::pair<int,unsigned> good(unsigned L) {
   return std::make_pair(0, L);
}

std::pair<int,unsigned> bad(unsigned L) {
   return std::make_pair<int,unsigned>(0, L);
}

clang++ -c -std=c++11 pp.cpp -stdlib=libc++
pp.cpp:8:10: error: no matching function for call to 'make_pair'
   return std::make_pair<int,unsigned>(0, L);
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/w/c/org/bin/../include/c++/v1/utility:639:1: note: candidate function not
       viable: no known conversion from 'unsigned int' to 'unsigned int &&' for
       2nd argument
make_pair(_T1&& __t1, _T2&& __t2)
^
1 error generated.

-Krzysztof

Hi Krzysztof,

The reason is that in C++03 make_pair looked like this

    template <class T1, class T2>
    pair<T1, T2> make_pair(T1 x, T2 y);

while in C++11 onwards looks like this

     template <class T1, class T2>
     pair<V1, V2> make_pair(T1&& x, T2&& y)

where in libcxx (similarly for libstdc++) V1 and V2 are implemented like

     pair<typename __make_pair_return<_T1>::type, typename __make_pair_return<_T2>::type>

because the C++ standard dictates a more sophisticated (compile-time computed) type for V1 and V2.

This implies that if you define T1 and T2 in the template-argument list of the function call, you are always going to get rvalue references in the parameter types (in your example 'int&&' and 'unsigned int&&' respectively).
Conversely, if you let template-argument deduction to infer T1 and T2, it will infer

  std::pair<int, unsigned int> std::make_pair<int, unsigned int &>(int &&, unsigned int &);

because of the C++11 rules about type deduction involving parameter types of the form Type&&.

I'm not an expert in this area but this behaviour looks to me as expected and matches the C++ view of generic programming in which you should not constraint (in this case using template-arguments) if not needed.

Hope this answers your question.

Kind regards,
Roger

This reminds me of STL's presentation "Don't help the compiler":

Csaba

More generally, the C++ standard library guarantees that specific expression forms will compile and have certain static and dynamic properties. If there isn't a listed expression form with explicit template arguments, they're not portably allowed, and including them means you're exceeding the guarantees of the standard library just as much as you would be if you tried to forward-declare std::basic_string, or took the address of std::sort, or spelled out a concrete result type for std::vector<bool>::operator.

John.

Thanks all, that explains it.

I do provide explicit arguments sometimes when the object types aren't quite what I want the compiler to use, for example:
   int x;
   unsigned y;
   std::make_pair<int,int>(x, y)
instead of
   std::make_pair(x, int(y)).

I suppose the latter may be the preferred version in the recent C++.

-Krzysztof

Thanks all, that explains it.

I do provide explicit arguments sometimes when the object types aren't quite what I want the compiler to use, for example:
int x;
unsigned y;
std::make_pair<int,int>(x, y)
instead of
std::make_pair(x, int(y)).

I suppose the latter may be the preferred version in the recent C++.

You can also just use std::pair<int,int>(x,y) if you're going to declare both arguments.

John.