将std::real作为投影传入ranges::sort排序复数向量编译失败的原因及解决
为什么用std::real作为投影对复数向量排序失败,如何解决?
尝试将std::real作为投影参数对复数向量按实部排序时,代码无法编译,但用lambda表达式可以正常运行;而类似的std::size作为投影对vector<vector<int>>排序却能正常编译。以下是测试代码:
#include <bits/stdc++.h> using namespace std; typedef complex<double> cp; int main() { vector<cp> P = {{2, 3}, {12, 30}, {40, 50}, {5, 1}, {12, 10}, {3, 4.2}}; ranges::sort(P, {}, real<double>); // 编译失败 ranges::sort(P, {}, real<cp>); // 编译失败 ranges::sort(P, {}, &cp::real); // 编译失败 auto pj = [](cp a) {return real(a);}; ranges::sort(P, {}, pj); // 编译通过且正常运行,但写法不够简洁 vector<vector<int>> tst = {{1, 3}, {1, 4, 5}, {1}}; ranges::sort(tst, {}, size<vector<int>>); // 编译通过且正常运行 ranges::sort(tst, {}, &vector<int>::size); // 编译通过且正常运行 auto tstpj = [](vector<int>& a) {return size(a);}; ranges::sort(tst, {}, tstpj); // 编译通过且正常运行,但写法不够简洁 }
原因分析
std::real的重载歧义:std::real是重载的函数模板,既有针对complex<T>的版本,也有适配普通数值类型的重载。直接传递real<double>或real<cp>时,编译器无法确定要选择哪个具体实例,导致匹配失败。而std::size针对容器的模板实例是唯一的,编译器能准确推导。- 成员函数适配问题:
cp::real是无参数的成员函数,需要绑定到对象实例才能调用,但ranges::sort要求投影是接受cp参数的一元可调用对象,直接传递&cp::real无法满足这个要求。 - 设计差异:
std::size针对容器的版本是接受容器引用的非成员函数,天然符合投影的一元调用要求;而std::real的非成员版本因重载过多,无法在传递模板实例时被明确选中。
解决方法
方法1:用std::ref明确指定std::real实例
通过std::ref消除重载歧义,让编译器确定具体的函数实例:
ranges::sort(P, {}, std::ref(std::real<cp>));
方法2:用std::mem_fn包装成员函数
将成员函数转换为可接受对象参数的一元可调用对象:
ranges::sort(P, {}, std::mem_fn(&cp::real));
方法3:用std::bind_front绑定函数参数
绑定std::real的模板实例,生成符合要求的投影对象:
ranges::sort(P, {}, std::bind_front(std::real<cp>));
方法4:自定义极简投影对象
用泛型lambda或结构体实现简洁的投影:
// 泛型lambda版本(C++14及以上) auto real_proj = [](const auto& c) { return std::real(c); }; ranges::sort(P, {}, real_proj); // 结构体版本 struct RealProj { double operator()(const cp& c) const { return std::real(c); } }; ranges::sort(P, {}, RealProj{});
内容的提问来源于stack exchange,提问作者Horst Kretschmer
相关产品推荐
相关产品推荐

