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

倖福魔咒の 提交于 2019-12-01 16:11:04

问题


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?


回答1:


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));



回答2:


The first member of the pair stored in map is const-qualified. So technically you need

get<0, const string, string>

But that does not limit the list of candidates to one unambiguous overload, since get is available in at least two versions: for const reference argument and for non-const reference argument.

You can choose one by using a cast

const string &(*g)(const pair<const string, string> &) = 
  get<0, const string, string>; 

or

typedef map<string, string> Map;

const Map::key_type &(*g)(const Map::value_type &) = 
  get<0, const Map::key_type, Map::mapped_type>; 

and then do

transform(mss.begin(), mss.end(), back_inserter(vs), g);


来源:https://stackoverflow.com/questions/27578773/why-cant-i-use-stdget0-in-stdtransform

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!