Search code examples
c++c++11overload-resolutionfunction-templates

Why can't I use std::get<0> in std::transform?


In trying to compile the following code which would copy a maps keys to a vector:

map<string, string> mss;
vector<string> vs;

transform(mss.begin(), mss.end(), back_inserter(vs), get<0>);

VS2013 can't distinguish which get is intended but this simpler usage works just fine:

vs.push_back(get<0>(*mss.begin()));

Specifying get<0, string, string> didn't help. What am I missing?


Solution

  • There are many overloads of std::get, where, in addition, each is a function template itself, therefore the compiler can't tell which one you want at the call site where you request for the address of one of them. If you insist on using std::get, you'd need to use static_cast:

    transform(mss.begin(), mss.end(), back_inserter(vs),
              static_cast<const map<string, string>::key_type&
                             (*)(map<string, string>::value_type&)>(std::get<0>)
                         );
    

    Which will work as long as the type in static_cast matches the declaration of a possible function template's specialization given as the argument. Also, you shoudn't try to explicitly specify the template arguments of function templates like get<0, string, string> etc. - this is what the template argument deduction mechanism is for. Not only is the syntax ugly, but there can be other overloads added in the future breaking your compilation.

    A much better alternative is to use a lambda expression:

    transform(mss.begin(), mss.end(), back_inserter(vs),
              [](map<string, string>::value_type& p){ return p.first; });
    

    or a generic lambda expression (C++14):

    transform(mss.begin(), mss.end(), back_inserter(vs),
              [](auto& p){ return p.first; }); // or `return std::get<0>(p);`
    

    or std::mem_fn which binds its argument to a given pointer to a data member or a member function:

    #include <functional>
    
    transform(mss.begin(), mss.end(), back_inserter(vs),
              mem_fn(&map<string, string>::value_type::first));