2 votes

std::ranges algorithme fonction objet points de personnalisation AKA niebloids - problèmes avec les types de la lib std

Le tout avec gcc 11.2 et libstdc++-11

Le code ci-dessous montre différentes façons d'utiliser std::sort y luego std::ranges::sort pour trier les types intégrés, les types définis par l'utilisateur et un type de bibliothèque standard.

Seul le type de bibliothèque std me pose problème :

  1. Je ne peux pas facilement définir un operator< o operator<=> car c'est l'UB qui l'ajoute à namespace std . Je pense qu'il n'y a aucun moyen de contourner ce problème ? (sans utiliser un lambda ou similaire)
  2. pour une raison quelconque, je ne peux pas passer une fonction membre publique en tant que fonction proj sur le type std lib (mais il fonctionne sur le type défini par l'utilisateur). Pourquoi ? Je n'ai pas réussi à pénétrer suffisamment le "mur d'erreurs" pour le découvrir. gcc dit en gros : no known conversion for argument 3 from ‘<unresolved overloaded function type>’ to ‘std::identity’ ..

Mise à jour : Ma seule théorie sur le point 2 ci-dessus est que "type de fonction surchargée non résolue" signifie que std::complex<double>::real a 2 surcharges. L'une prend un paramètre (le "setter") et l'autre n'en prend pas (le "getter"). Existe-t-il une syntaxe qui me permette de spécifier que je veux l'"adresse de" celle qui ne prend pas de paramètre ?

Mise à jour2 : Merci d'avoir signalé dans les commentaires que le fait de prendre l'adresse d'une fonction membre en std n'est de toute façon que de l'UB. Cependant, si j'ajoute un sum(int c) surcharge à thing (ajouté ci-dessous), j'obtiens la même erreur "unresolved overloaded". La question reste donc de savoir comment sélectionner celui qui n'a pas de paramètres. Ou n'y a-t-il aucun moyen ?

#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;
}

1voto

Caleth Points 17517

Vous devez spécifier une comparaison pour T si std::less<T> n'est pas disponible.

Les deux std::sort y std::ranges::sort nécessitent un prédicat d'ordre faible strict, et non pas un prédicat d'ordre faible strict. operator<=> (mais qui peut en fournir un, via < y 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X