c++templatesreferencervaluelvalue

Why doesn't make_pair<string, string>() call the copy contructor, when given const string&?


Both GCC and Clang refuse to compile this one:

#include <string>
#include <utility>

using namespace std;

int main() {
    const string s = "12345";
    const string& r = s;

    auto p = std::make_pair<string, string>(r, r);
}

GCC says:

error: cannot bind rvalue reference of type ‘std::__cxx11::basic_string<char>&&’ to lvalue of type ‘const std::string’ {aka ‘const std::__cxx11::basic_string<char>’}

While Clang says:

error: no matching function for call to 'make_pair'

Since I'm giving make_pair explicit types, why doesn't it construct new strings from const string&?


This one compiles:

auto p = std::make_pair<string, string>(string(r), string(r));

Solution

  • Assuming C++11 or later:

    std::make_pair is not supposed to be used with explicitly-specified template arguments. They are intended to be deduced from the function arguments via forwarding references. The signature of std::make_pair is

    template<class T1, class T2>
    constexpr std::pair<V1, V2> make_pair(T1&& t, T2&& u);
    

    This shows that T1 and T2 are used as forwarding references and therefore shouldn't be specified explicitly. (V1/V2 are computed from T1/T2 by decay.)

    Explicitly specifying the template arguments breaks the forwarding behavior. With string as template argument you get string&& in the function parameter, which is a rvalue reference that doesn't accept lvalues. You would need to provide const string& as template argument for T to make T&& also const string&.

    But don't do that and just write

    auto p = std::make_pair(r, r);
    

    as is intended usage.


    Before C++11 there were no forwarding references and std::make_pair looked like this:

    template <class T1, class T2>
    std::pair<T1, T2> make_pair(T1 t, T2 u);
    

    So the code from your question would compile prior to C++11. Still, specifying the template arguments was redundant then as well.