tags:

views:

190

answers:

4

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?

+11  A: 

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*).
Potatoswatter
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
@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
"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
+1  A: 

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

Michael Burr
+2  A: 

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) { }
Eddy Pronk
Technically speaking the standard says that a copy constructor for class `X` takes a single parameter of type `X`, 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
+9  A: 
Noah Roberts
+0 It doesn't matter if the constructor of pair is implicit or not. I made it explicit and it still works.
Eddy Pronk
To elaborate, the constructor is being explicitly called in the initialization list; no implicit conversion is occurring.
GMan
@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.
Noah Roberts
@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.
Noah Roberts
@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
@eddie - I don't know what you did so I can't tell what you might have done wrong. I also don't know what compiler you are using so I can't tell if it's broken. What I can tell you is that you're wrong. See 12.3.1/2: "An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used." I also don't see any potatohead but it doesn't matter. I don't know why you think I'm wrong but the standard text and every tutorial and book on the matter proves me out.
Noah Roberts