Вопрос о std :: less поведение - PullRequest
9 голосов
/ 07 февраля 2011

Что там происходит?

#include <functional>

namespace A {
    struct Class { };
}

bool operator<(const A::Class& a, const A::Class& b)
{ return false; }

int main()
{
    std::less<A::Class>()(A::Class(), A::Class());
    return 0;
}

Это скомпилировано нормально.Но если я использую.

#include <set>

Я получил ошибки:

g++     test.cc   -o test
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/bits/stl_tree.h:64:0,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/set:60,
                 from lookup.cc:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/bits/stl_function.h: In member function 'bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A::Class]':
test.cc:15:49:   instantiated from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/bits/stl_function.h:230:22: error: no match for 'operator<' in '__x < __y'
make: *** [test] Error 1

Ответы [ 3 ]

11 голосов
/ 07 февраля 2011

Причиной сбоя поиска является то, что set вводит operator< для std::set в пространстве имен std, которое скрывает все остальные глобальные operator<.

Поиск < в экземпляре std::less происходит внутри области видимости внутри пространства имен std. Единственный способ, которым любое operator< за пределами std пространства имен станет видимым, это если ADL вступит в действие, и это произойдет только для ближайшего окружающего пространства имен.

Без включения <set>, не введено operator< (и это, вероятно, зависит от реализации) в пространстве имен std, которое скрывает глобальный operator<, и, следовательно, правила поиска без ADL все еще могут находить глобальные operator< которые берут A::Class.

Исправление: Как указывает @JohannesSchaub, этот анализ будет правильным, только если объявление operator< произошло до того, как <functional> (где определено std::less) было впервые включено. Таким образом, единственными перегрузками функции, вызываемой через unqualified-id , которые должны быть видны при поиске без ADL внутри определения шаблона, являются те, которые видны в точке определения. Определения, введенные между точкой определения и точкой создания, должны быть видимы только для поиска ADL. (В выражении, таком как x < y, ищутся функции-кандидаты с именем operator<, и это одна конкретная форма unqualified-id .)

В существующем виде перегруженный operator< не должен рассматриваться как кандидат с или без включения <set>, хотя эти угловые случаи в правилах поиска не всегда корректно обрабатываются всеми компиляторами.

5 голосов
/ 07 февраля 2011

То, что operator< также должно быть в namespace A, иначе его нельзя найти.

Детали: Прежде всего, просто вызов operator< для двух Class объектов из main()или даже реализация собственного less, конечно, работает независимо от того, находится ли operator< в том же пространстве имен, что и Class, или нет, хотя должен находиться в одном и том же пространстве имен, поскольку это то, что каждый, включая разработчиковиз библиотек, ожидает.И gcc, и MSVC 2010 (как я только что тестировал) включают несколько дополнительных operator< в свои стандартные библиотеки, между которыми не удается разрешить компилятор. Сообщение об ошибке

gcc 4.5.2 обманчиво.Скомпилировав то же самое с предварительной версией 4.6, я получаю более конкретную

In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_tree.h:65:0,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/set:60,
                 from test.cc:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_function.h: In member function 'bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A::Class]':
test.cc:9:42:   instantiated from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_function.h:234:22: error: no match for 'operator<' in '__x < __y'
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_function.h:234:22: note: candidates are:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_pair.h:205:67: note: template<class _T1, class _T2> bool std::operator<(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_iterator.h:290:46: note: template<class _Iterator> bool std::operator<(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_Iterator>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_iterator.h:340:47: note: template<class _IteratorL, class _IteratorR> bool std::operator<(const std::reverse_iterator<_IteratorL>&, const std::reverse_iterator<_IteratorR>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_tree.h:847:70: note: template<class _Key, class _Val, class _KeyOfValue, class _Compare, class _Alloc> bool std::operator<(const std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&, const std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_set.h:712:46: note: template<class _Key, class _Compare, class _Alloc> bool std::operator<(const std::set<_Key, _Compare, _Alloc>&, const std::set<_Key, _Compare, _Alloc>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_multiset.h:695:51: note: template<class _Key, class _Compare, class _Alloc> bool std::operator<(const std::multiset<_Key, _Compare, _Alloc>&, const std::multiset<_Key, _Compare, _Alloc>&)

Кстати, SunCC (как с rw, так и с stlport4) компилирует это чисто и даже создает полезную std::set<A::Class>.

3 голосов
/ 07 февраля 2011

Ваша перегрузка operator< должна находиться в том же пространстве имен, что и Class (то есть она также должна находиться в пространстве имен A).

C ++ имеет сложные правила поиска имен.Одной из наиболее интересных «функций» является «поиск, зависящий от аргумента», или ADL, где имена можно искать в пространствах имен, связанных с аргументами функции.Для класса, который не имеет базовых классов и не является вложенным классом, как ваш Class, единственным связанным пространством имен является пространство имен, в котором находится класс.

Итак, единственное пространство имен, связанное с Class - это A, поэтому во время поиска, зависящего от аргумента, для перегрузки operator< ищется только пространство имен A.

Ответ Чарльза Бейли дает хорошее объяснение того, почемувидеть проблему только при включении <set>.

...