J'ai des structures comme celle-ci (types simplifiés pour poursuivre l'objectif), qui vivent dans un système de gestion de l'information. std::vector
:
struct Region {
int first;
int count;
struct Metadata region_metadata;
};
Dans le vecteur, ils sont classés par first
. Si vous ajoutez first
et count
vous obtenez le first
de la région suivante ; en gros, ce vecteur de structs décrit les métadonnées pour des plages contiguës de nombres.
Maintenant, étant donné un nombre entier, je veux rechercher les métadonnées. Comme les régions sont triées, je peux utiliser std::upper_bound
. Je l'ai mis en œuvre de cette façon :
struct Comp
{
inline bool operator()(const Region ®ion, int index) const
{
return region.first < index;
}
inline bool operator()(int index, const Region ®ion) const
{
return index < region.first;
}
};
Cela fonctionne, lorsque l'on appelle std::upper_bound
avec :
auto iter = std::upper_bound(m_regions.begin(),
m_regions.end(),
index,
Comp());
Il se trouve que ça marche, parce que upper_bound
peut choisir en interne la surcharge qui correspond à ses besoins, car il appelle les deux Comp()(Region, int)
et Comp()(int, Region)
(c'est la raison pour laquelle un [](const Region ®, int index){…}
ne fonctionnerait pas).
En fait, j'ai trouvé la solution en retraçant les messages d'erreur lors de l'utilisation du lambda que j'ai mentionné précédemment. Le site docs pour std::upper_bound à cppreference.com écrire sur le quatrième argument :
objet de la fonction de comparaison (c'est-à-dire un objet qui satisfait aux exigences de exigences de Compare) qui renvoie vrai si le premier argument est inférieur au second.
La signature de la fonction de comparaison doit être équivalente à celle de la fonction de comparaison de l'UE. suivante :
bool cmp(const Type1 &a, const Type2 &b);
La signature ne doit pas nécessairement avoirconst &
mais l'objet fonction ne doit pas modifier les objets qui lui sont passés. Les typesType1
etType2
doit être tel qu'un objet de typeT
peut être implicitement converti en les deuxType1
etType2
et un objet de typeForwardIt
peut être déréférencée et ensuite implicitement convertie à la fois enType1
etType2
.Le type
Type1
doit être tel qu'un objet de typeT
peut être implicitement converti enType1
. Le typeType2
doit être telle qu'une objet de typeForwardIt
peut être déréférencé et ensuite implicitement converti enType2
.
(cppreference a été fixé depuis que j'ai posté cette question, merci @T.C.)
Ici, T
est le troisième argument de std::upper_bound
et ForwardIt
est le type des deux premiers arguments. Cette citation ne parle pas du fait que l'objet de la fonction est en fait un struct qui surcharge sa fonction operator()
pour couvrir les situations "avant" et "arrière".
Donc, dans les Règles-Ecrites, est-ce légal, ou est-ce un artefact de ma combinaison spécifique de compilateur/bibliothèque standard (g++ 5.3.1) ?
Je suis intéressé par les réponses spécifiques à C++14 ou C++17.
Exemple complet :
#include <algorithm>
#include <iostream>
#include <vector>
struct Region {
Region(int first, int count, int n):
first(first),
count(count),
n(n)
{
}
int first;
int count;
int n; // think struct Metadata
};
struct Comp
{
inline bool operator()(const Region ®ion, int index) const
{
return region.first < index;
}
inline bool operator()(int index, const Region ®ion) const
{
return index < region.first;
}
};
int main() {
std::vector<Region> regions;
regions.emplace_back(0, 10, 1);
regions.emplace_back(10, 10, 2);
regions.emplace_back(20, 10, 3);
const int lookup = 10;
auto iter = std::upper_bound(
regions.begin(),
regions.end(),
lookup,
Comp());
// yes, I omitted error checking here, with error being iter == regions.begin()
std::cout << lookup << " is in region with n = " << (iter-1)->n << std::endl;
}