У меня есть такие структуры (типы, упрощенные для переноса точки), живущие в std::vector
:
struct Region {
int first;
int count;
struct Metadata region_metadata;
};
В векторе они упорядочиваются на first
. Если вы добавите first
и count
, вы получите first
следующего региона; поэтому в основном этот вектор структур описывает метаданные для смежных диапазонов чисел.
Теперь, учитывая целое число, я хочу посмотреть метаданные. Когда регионы отсортированы, я могу использовать std::upper_bound
. Я реализовал его следующим образом:
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;
}
};
Это работает при вызове std::upper_bound
с:
auto iter = std::upper_bound(m_regions.begin(),
m_regions.end(),
index,
Comp());
Теперь это происходит, потому что upper_bound
может внутренне выбирать перегрузку, которая соответствует его требованиям, так как она вызывает как Comp()(Region, int)
, так и Comp()(int, Region)
(из-за чего не работает [](const Region ®, int index){…}
).
Я действительно придумал решение, отслеживая сообщения об ошибках при использовании лямбда, о которой я упоминал ранее. docs для std:: upper_bound на cppreference.com напишите о четвертом аргументе:
объект функции сравнения (т.е. объект, который удовлетворяет требования Compare), который возвращает true, если первый аргумент меньше второго.
Подпись функции сравнения должна быть эквивалентна следующее:
bool cmp(const Type1 &a, const Type2 &b);
Подписи не требуетсяconst &
, но объект функции не должны изменять переданные ему объекты. ТипыType1
иType2
должен быть таким, чтобы объект типаT
мог быть неявно преобразован в какType1
иType2
, а объектом типаForwardIt
может быть разыменовывается и затем неявно преобразуется в обаType1
иType2
.Тип
Type1
должен быть таким, чтобы объект типаT
мог быть неявно преобразованный вType1
. ТипType2
должен быть таким, чтобы объект типаForwardIt
может быть разыменован, а затем неявно преобразован вType2
.
(cppreference исправлено, так как я разместил этот вопрос, спасибо @T.C.)
Здесь T
является третьим аргументом std::upper_bound
и ForwardIt
является типом первых двух аргументов. В этой цитате не говорится о функциональном объекте, фактически являющемся структурой, которая перегружает свой operator()
для покрытия "вперед" и "обратных" ситуаций.
Итак, в Rules-As-Written это законно, или это артефакт моей конкретной комбинации компилятора/стандартной библиотеки (g++ 5.3.1)?
Меня интересуют ответы, специфичные для С++ 14 или С++ 17.
Полный пример:
#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;
}