Question

The definition for make_pair in the MSVC++ "utility" header is:

template<class _Ty1,
 class _Ty2> inline
 pair<_Ty1, _Ty2> make_pair(_Ty1 _Val1, _Ty2 _Val2)
 { // return pair composed from arguments
 return (pair<_Ty1, _Ty2>(_Val1, _Val2));
 }

I use make_pair all the time though without putting the argument types in angle brackets:

    map<string,int> theMap ;

    theMap.insert( make_pair( "string", 5 ) ) ;

Shouldn't I have to tell make_pair that the first argument is std::string and not char* ?

How does it know?

Was it helpful?

Solution

Function template calls can usually avoid explicit template arguments (ie make_pair<…>) by argument deduction, which is defined by C++03 §14.8.2. Excerpt:

When a function template specialization is referenced, all of the template arguments must have values. The values can be either explicitly specified or, in some cases, deduced from the use.

The specific rules are a bit complicated, but typically it "just works" as long as you have only one specialization which is generally qualified enough.

Your example uses two steps of deduction and one implicit conversion.

  • make_pair returns a pair<char const*, int>,
  • then template<class U, classV> pair<string,int>::pair( pair<U,V> const & ) kicks in with U = char*, V = int and performs member-wise initialization,
  • invoking string::string(char*).

OTHER TIPS

It doesn't. make_pair generated a pair<char*,int> (or maybe a pair<char const*,int>).

However, if you'll note in the implementation of pair there's a templated copy constructor:


template < typename Other1, typename Other2 > 
pair(pair<Other1,Other2>& other) 
  : first(other.first), second(other.second)
{}

This may be implemented in slightly different ways but amounts to the same thing. Since this constructor is implicit, the compiler attempts to create pair<std::string,int> out of your pair<char*,int> - since the necessary types are convertible this works.

make_pair() exists precisely so that argument type deduction can be used to determine the template parameter types.

See this SO question: Using free function as pseudo-constructors to exploit template parameter deduction

It relies on the fact that the constructor of std::string accepts a const char*. It doesn't matter if this constructor of std::string is explicit or not. The template deducts the type and uses the copy constructor of pair to convert it. It also doesn't matter whether or not the pair constructor is explicit.

If you turn the constructor of std::string into:

class string
{
public:
    string(char* s)
    {
    }   
};

you get this error:

/usr/include/c++/4.3/bits/stl_pair.h: In constructor ‘std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U1 = const char*, _U2 = int, _T1 = const string, _T2 = int]’:
foo.cpp:27:   instantiated from here
/usr/include/c++/4.3/bits/stl_pair.h:106: error: invalid conversion from ‘const char* const’ to ‘char*’
/usr/include/c++/4.3/bits/stl_pair.h:106: error:   initializing argument 1 of ‘string::string(char*)’

The constructor looks like this:

  template<class _U1, class _U2>
    pair(const pair<_U1, _U2>& __p)
    : first(__p.first),
      second(__p.second) { }

The copy constructor looks like this:

template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
    : first(__p.first),
      second(__p.second) { }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top