std::ranges 算法函数对象定制点 AKA niebloids - std lib 类型的问题
std::ranges algorithm function object customisation points AKA niebloids - problems with std lib types
全部通过 gcc 11.2 和 libstdc++-11 完成
下面的代码展示了使用 std::sort
和 std::ranges::sort
对内置函数、用户定义类型和标准库类型进行排序的多种方式。
只有 std 库类型给我带来麻烦:
- 我无法轻松定义自定义
operator<
或 operator<=>
,因为将其添加到 namespace std
是 UB。我认为没有办法解决这个问题? (不使用 lambda 或类似函数)
- 出于某种原因,我无法将 public 成员函数作为标准库类型的
proj
参数传递(但它适用于用户定义的类型)。为什么?我无法穿透“错误之墙”来找出答案。 gcc 基本上说:no known conversion for argument 3 from ‘<unresolved overloaded function type>’ to ‘std::identity’
..
更新:我对上面 2 的唯一理论是“未解析的重载函数类型”意味着 std::complex<double>::real
有 2 个重载。一个接受参数(“setter”)和一个不接受参数(“getter”)。是否有一种语法允许我指定我想要没有参数的“地址”?
更新2:感谢康桐蔚在评论中指出,在std
中获取成员函数的地址只是UB。但是,如果我将 sum(int c)
重载添加到 thing
(现在在下面添加),我会得到相同的“未解决的重载”错误。所以问题仍然存在,我如何 select 没有参数的那个。还是没有办法?
#include <algorithm>
#include <compare>
#include <complex>
#include <iostream>
#include <ranges>
#include <vector>
namespace std {
// this is UNDEFINED BEHAVIOUR!!! --- but "it works", so we know this option is what would be
// required, but is not available to us
std::partial_ordering operator<=>(const std::complex<double>& a, const std::complex<double>& b) {
return std::abs(a) <=> std::abs(b);
}
} // namespace std
// a user defined type
struct thing {
int x{};
int y{};
[[nodiscard]] int sum() const { return x + y; }
[[nodiscard]] int sum(int c) const { return x + y + c; } // added for update 2
friend std::strong_ordering operator<=>(const thing& a, const thing& b) {
return a.x + a.y <=> b.x + b.y;
}
friend bool operator==(const thing& a, const thing& b) { return a.x + a.y == b.x + b.y; }
friend std::ostream& operator<<(std::ostream& os, const thing& rhs) {
return os << "[" << rhs.x << "," << rhs.y << "]";
}
};
int main() {
// builtin types
auto ints = std::vector<int>{9, 10, 7, 8, 5, 6, 3, 4, 1, 2};
std::ranges::sort(ints);
std::ranges::sort(ints, {}, [](const auto& c) { return -c; });
for (const auto& e: ints) std::cout << e << " ";
std::cout << "\n";
auto things = std::vector<thing>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(things.begin(), things.end());
std::ranges::sort(things);
std::ranges::sort(things, {}, [](const auto& e) { return e.sum(); });
std::ranges::sort(things, [](const auto& a, const auto& b) { return a < b; }, {});
std::ranges::sort(things, {}, &thing::x);
std::ranges::sort(things, {}, &thing::sum); // COMPILE ERROR afte r update 2
for (const auto& e: things) std::cout << e << " ";
std::cout << "\n";
auto complexes = std::vector<std::complex<double>>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(complexes.begin(), complexes.end()); // requires operator< or <=> which is UB
std::ranges::sort(complexes); // requires operator<=> which is UB
std::ranges::sort(complexes, {}, [](const auto& c) { return std::abs(c); });
std::ranges::sort(complexes, {}, &std::complex<double>::real); // COMPILE ERROR!!
for (const auto& e: complexes) std::cout << e << " ";
std::cout << "\n";
return EXIT_SUCCESS;
}
如果 std::less<T>
不可用,您需要为 T
指定比较。
std::sort
和 std::ranges::sort
都需要一个严格的弱排序谓词,而不是 operator<=>
(但可以通过 <
和 std::less
提供一个)
template<typename T>
bool complex_less (std::complex<T> lhs, std::complex<T> rhs) { return abs(lhs) < abs(rhs); };
int main() {
auto things = std::vector<thing>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(things.begin(), things.end());
std::ranges::sort(things);
std::ranges::sort(things, {}, [](const auto& e) { return e.sum(); });
std::ranges::sort(things, [](const auto& a, const auto& b) { return a < b; }, {});
std::ranges::sort(things, {}, &thing::x);
std::ranges::sort(things, {}, static_cast<int (thing::*)()>(&thing::sum)); // need cast to disambiguate pointer-to-member
for (const auto& e: things) std::cout << e << " ";
std::cout << "\n";
auto complexes = std::vector<std::complex<double>>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(complexes.begin(), complexes.end(), complex_less<double>); // fine
std::ranges::sort(complexes, complex_less<double>); // also fine
std::ranges::sort(complexes, {}, [](const auto& c) { return std::abs(c); }); // still fine
std::ranges::sort(complexes, {}, [](const auto& c) { return c.real(); }); // fine
for (const auto& e: complexes) std::cout << e << " ";
std::cout << "\n";
return EXIT_SUCCESS;
}
全部通过 gcc 11.2 和 libstdc++-11 完成
下面的代码展示了使用 std::sort
和 std::ranges::sort
对内置函数、用户定义类型和标准库类型进行排序的多种方式。
只有 std 库类型给我带来麻烦:
- 我无法轻松定义自定义
operator<
或operator<=>
,因为将其添加到namespace std
是 UB。我认为没有办法解决这个问题? (不使用 lambda 或类似函数) - 出于某种原因,我无法将 public 成员函数作为标准库类型的
proj
参数传递(但它适用于用户定义的类型)。为什么?我无法穿透“错误之墙”来找出答案。 gcc 基本上说:no known conversion for argument 3 from ‘<unresolved overloaded function type>’ to ‘std::identity’
..
更新:我对上面 2 的唯一理论是“未解析的重载函数类型”意味着 std::complex<double>::real
有 2 个重载。一个接受参数(“setter”)和一个不接受参数(“getter”)。是否有一种语法允许我指定我想要没有参数的“地址”?
更新2:感谢康桐蔚在评论中指出,在std
中获取成员函数的地址只是UB。但是,如果我将 sum(int c)
重载添加到 thing
(现在在下面添加),我会得到相同的“未解决的重载”错误。所以问题仍然存在,我如何 select 没有参数的那个。还是没有办法?
#include <algorithm>
#include <compare>
#include <complex>
#include <iostream>
#include <ranges>
#include <vector>
namespace std {
// this is UNDEFINED BEHAVIOUR!!! --- but "it works", so we know this option is what would be
// required, but is not available to us
std::partial_ordering operator<=>(const std::complex<double>& a, const std::complex<double>& b) {
return std::abs(a) <=> std::abs(b);
}
} // namespace std
// a user defined type
struct thing {
int x{};
int y{};
[[nodiscard]] int sum() const { return x + y; }
[[nodiscard]] int sum(int c) const { return x + y + c; } // added for update 2
friend std::strong_ordering operator<=>(const thing& a, const thing& b) {
return a.x + a.y <=> b.x + b.y;
}
friend bool operator==(const thing& a, const thing& b) { return a.x + a.y == b.x + b.y; }
friend std::ostream& operator<<(std::ostream& os, const thing& rhs) {
return os << "[" << rhs.x << "," << rhs.y << "]";
}
};
int main() {
// builtin types
auto ints = std::vector<int>{9, 10, 7, 8, 5, 6, 3, 4, 1, 2};
std::ranges::sort(ints);
std::ranges::sort(ints, {}, [](const auto& c) { return -c; });
for (const auto& e: ints) std::cout << e << " ";
std::cout << "\n";
auto things = std::vector<thing>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(things.begin(), things.end());
std::ranges::sort(things);
std::ranges::sort(things, {}, [](const auto& e) { return e.sum(); });
std::ranges::sort(things, [](const auto& a, const auto& b) { return a < b; }, {});
std::ranges::sort(things, {}, &thing::x);
std::ranges::sort(things, {}, &thing::sum); // COMPILE ERROR afte r update 2
for (const auto& e: things) std::cout << e << " ";
std::cout << "\n";
auto complexes = std::vector<std::complex<double>>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(complexes.begin(), complexes.end()); // requires operator< or <=> which is UB
std::ranges::sort(complexes); // requires operator<=> which is UB
std::ranges::sort(complexes, {}, [](const auto& c) { return std::abs(c); });
std::ranges::sort(complexes, {}, &std::complex<double>::real); // COMPILE ERROR!!
for (const auto& e: complexes) std::cout << e << " ";
std::cout << "\n";
return EXIT_SUCCESS;
}
如果 std::less<T>
不可用,您需要为 T
指定比较。
std::sort
和 std::ranges::sort
都需要一个严格的弱排序谓词,而不是 operator<=>
(但可以通过 <
和 std::less
提供一个)
template<typename T>
bool complex_less (std::complex<T> lhs, std::complex<T> rhs) { return abs(lhs) < abs(rhs); };
int main() {
auto things = std::vector<thing>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(things.begin(), things.end());
std::ranges::sort(things);
std::ranges::sort(things, {}, [](const auto& e) { return e.sum(); });
std::ranges::sort(things, [](const auto& a, const auto& b) { return a < b; }, {});
std::ranges::sort(things, {}, &thing::x);
std::ranges::sort(things, {}, static_cast<int (thing::*)()>(&thing::sum)); // need cast to disambiguate pointer-to-member
for (const auto& e: things) std::cout << e << " ";
std::cout << "\n";
auto complexes = std::vector<std::complex<double>>{{9, 10}, {7, 8}, {3, 4}, {1, 2}, {5, 6}};
std::sort(complexes.begin(), complexes.end(), complex_less<double>); // fine
std::ranges::sort(complexes, complex_less<double>); // also fine
std::ranges::sort(complexes, {}, [](const auto& c) { return std::abs(c); }); // still fine
std::ranges::sort(complexes, {}, [](const auto& c) { return c.real(); }); // fine
for (const auto& e: complexes) std::cout << e << " ";
std::cout << "\n";
return EXIT_SUCCESS;
}