Take the 2-minute tour ×

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?

share|improve this question
gcc has this in stl_pair.h. –  Eddy Pronk Apr 20 '10 at 0:15
add comment

4 Answers

up vote 11 down vote accepted

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*).
share|improve this answer
The first part is misleading, but the second part is right on: you don´t tell make_pair the type, the templated constructor in pair takes care of the type conversion. –  David Rodríguez - dribeas Apr 20 '10 at 7:48
@David: Can you be more specific about what's misleading? There are two deduction steps, one of which is make_pair figuring out its own return type. –  Potatoswatter Apr 20 '10 at 8:00
"It's a perfect example of template argument deduction"... The question is how make_pair deduces that the first element is a string and the answer is that it does not, not that it is a "perfect example". There is a two step process involved, and the second step is the one that performs the conversion. I upvoted as the second half of the question is precise and correct. (Probably misleading is the wrong word and misleading itself :) ). –  David Rodríguez - dribeas Apr 20 '10 at 9:17
add comment

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.

share|improve this answer
+0 It doesn't matter if the constructor of pair is implicit or not. I made it explicit and it still works. –  Eddy Pronk Apr 19 '10 at 23:46
To elaborate, the constructor is being explicitly called in the initialization list; no implicit conversion is occurring. –  GManNickG Apr 20 '10 at 3:30
@eddy - Just because you made it explicit and the code "worked" doesn't mean what happened is what you think happened. Try making your own type like pair with only the initial constructor and an explicit conversion copy constructor like pair has. Unless your compiler is broken you'll find that "explicit" makes the code fail. @GMan - I was talking about the constructor for pair, not the construction call for std::string in the initialization list. –  Crazy Eddie Apr 20 '10 at 15:55
@eddie - to expand, the whole purpose of the explicit keyword is to disallow the undesired conversion of types by calling a function that expects type X with type Y that X can be constructed from. In other words, it's meant to disallow the very type of code being discussed. As such, if you really made that constructor in std::pair explicit and the call still worked there is something going on other than what you think is going on. –  Crazy Eddie Apr 20 '10 at 16:06
@noah - I did create my own pair like you suggested and made the constructor explicit. It doesn't rely on the implicit conversion. See the answer of @potatohead. –  Eddy Pronk Apr 20 '10 at 23:00
show 1 more comment

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

share|improve this answer
add comment

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
    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) { }
share|improve this answer
Technically speaking the standard says that a copy constructor for class X takes a single parameter of type X& or X const &. A templated constructor cannot be a copy constructor ever. (That is, if class X defines template <typename T> X( T& );, even if X x( f() ); with f returning X matches the templated cosntructor, the compiler will use the implicitly generated copy constructor instead of the explicitly defined templated constructor.) –  David Rodríguez - dribeas Apr 20 '10 at 8:00
add comment

Your Answer


