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::sortstd::ranges::sort 对内置函数、用户定义类型和标准库类型进行排序的多种方式。

只有 std 库类型给我带来麻烦:

  1. 我无法轻松定义自定义 operator<operator<=>,因为将其添加到 namespace std 是 UB。我认为没有办法解决这个问题? (不使用 lambda 或类似函数)
  2. 出于某种原因,我无法将 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::sortstd::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;
}