C++中不使用lambda能否传递std::get等标准函数模板作为参数
问题解答
你的猜测完全正确,直接传递std::get<0, str_and_int>编译失败的核心原因就是 std::get存在多重重载,编译器无法在函数传参的泛型推导上下文里,唯一确定你需要匹配的重载版本。
即使你显式指定了模板参数<0, str_and_int>,std::get仍然会生成至少四个不同签名的重载,分别对应接收左值、const左值、右值、const右值的tuple参数,它们对应完全不同的函数地址,std::transform在推导谓词类型时无法自动选出符合要求的版本,就会触发编译错误。
不使用lambda的可行实现方案
- 显式转换为匹配签名的函数指针
通过static_cast明确指定你要取的std::get重载的函数签名,编译器就会精准匹配对应版本,这种写法在所有主流STL实现中都可以正常工作:
std::vector<std::string> just_strings(const std::vector<str_and_int>& tuples) { std::vector<std::string> strings(tuples.size()); std::transform(tuples.begin(), tuples.end(), strings.begin(), static_cast<const std::string& (*)(const str_and_int&)>(&std::get<0, str_and_int>) ); return strings; }
注意cast里的函数签名必须和传入参数的类型完全匹配:这里传入的是const str_and_int&类型的元素,返回的是tuple第0位的const引用,签名对应准确才不会出现编译错误。
提示:这种写法存在极小的可移植性风险——C++标准只规定了
std::get的调用行为,没有强制要求它必须以独立函数的形式实现,如果某款STL实现把std::get做成了带多个调用运算符的函数对象,取函数地址的写法就会失效,不过目前所有主流STL都没有采用这种实现。
- C20及以上版本使用标准范围适配器
如果可以使用C20标准,直接用标准库自带的std::views::elements<N>适配器即可,它的设计目的就是提取tuple/pair等类型的指定位置元素,完全没有重载歧义,代码也更简洁,是标准保证可移植的写法:
#include <ranges> std::vector<std::string> just_strings(const std::vector<str_and_int>& tuples) { auto strs = tuples | std::views::elements<0>; return {strs.begin(), strs.end()}; }
这种写法甚至不需要手动调用std::transform和提前分配目标vector的大小,语义也更直观。
内容的提问来源于stack exchange,提问作者jwezorek
相关产品推荐
相关产品推荐

