2014-11-04 1 views
4

Это может быть задано раньше, но мне не повезло найти ответ ...понимание поиска оператора; какой компилятор прав?

У меня есть неупорядоченный контейнер (то есть хеш, мы будем называть его QHash, потому что это, хотя это, вероятно, происходит в любая аналогичная ситуация), для которой нужен оператор сравнения для его ключевого типа.

Рассмотрим следующий пример:

// foo.h 
class Bar 
{ 
public: 
    class Foo {}; 
}; 

// foo.cpp 
#include <QtCore/QHash> 

namespace 
{ 
    typedef Bar::Foo Foo; 
    bool operator==(Foo const& a, Foo const& b) { return &a == &b; } 
} 

uint qHash(Foo const& foo) { return qHash(&foo); } 

int main() 
{ 
    QHash<Foo, int> hash; 
    // do stuff with hash, e.g.: 
    hash.insert(Foo(), 5); 
    return 0; 
} 

Используя G ++, все хорошо. Тем не менее, clang дает ошибку в недрах qhash.h около invalid operands to binary expression, где пытается использовать == для экземпляров Foo. Мне кажется, что clang не находит или отвергает определение operator== в анонимном пространстве имен, вероятно, из-за разных правил поиска, кроме G ++.

Мне интересно, какой компилятор прав?

p.s. Я строю в режиме C++ 11, если это имеет значение.

+0

Содержание * делаю * пункт. Нам понадобится настоящий сборник и сообщения об ошибках. – jrok

+3

Хорошая идея, чтобы читатели SO догадывались о том, какой секретный код произвел неопределенное описание поведения. Я почти проголосовал за это. Я вижу, что, по крайней мере, 4 человека уже сделали это, поэтому я воздерживаюсь. –

+0

@jrok, нет, на самом деле они этого не делают (на самом деле, пустая будет хорошо). Вопрос в том, что: «учитывая вышеизложенное, ожидается ли, что QHash, пытающийся использовать оператор == на паре Foo, найдет оператор, как он определен?». GCC думает «да». Кланг считает «нет». Я * не спрашиваю, как это исправить («не делай этого»/сделай оператор == общедоступным); Мне интересно, что правильно. – Matthew

ответ

5

Ну, ADL смотрит в пространство имен класса определяется в Посмотрите на [basic.lookup.argdep]/2:. «Для каждого типа аргумента T в вызове функции, существует множество нуля или более связанных пространств имен и набор нулевых или более связанных классов, которые необходимо учитывать. Наборы пространств имен и классов определяются полностью типами аргументов функции (и пространства имен любых аргументов шаблона шаблона). Typedef имена и декларации использования, используемые для указания типов, не включают . »

Отметить последнее предложение.

Таким образом, для исходного примера, typedef в пространстве имен оператора не помогает, gcc ошибочен, а clang является правильным.

+0

Thanks; это было пятно! – Matthew

+0

За исключением того, что пространство имен, в котором оператор * * используется *, также влияет на поиск, и OP не сообщал нам, какое пространство имен вызывало код. –

-1

Несмотря на то, что выбор неупорядоченного контейнера не имеет значения, следующий автономный полностью стандартный код генерирует согласованные ошибки как для g ++, так и для clang - ни один не находит operator==.

#include <unordered_map> 

// foo.h 
class Bar 
{ 
public: 
    class Foo {}; 
}; 

// foo.cpp 
namespace 
{ 
    typedef Bar::Foo Foo; 
    bool operator==(Foo const& a, Foo const& b) { return &a == &b; } 
} 

namespace std 
{ 
    template <> 
    struct hash<Foo> 
    { 
    std::size_t operator()(const Foo& k) const 
    { 
     return std::hash<const Foo*>()(&k); 
    } 
    }; 
} 

int main() 
{ 
    std::unordered_map<Foo, int> hash; 
    // do stuff with hash, e.g.: 
    hash.emplace(Foo(), 5); 
    return 0; 
} 

Важной частью ошибки

error: no match for operator== (operand types are const Bar::Foo and const Bar::Foo)

{ return __x == __y; } 

В принципе, если вы хотите, чтобы операторы будут найдены библиотеки кода, вставить их в соответствующем пространстве имен класса и пусть ADL делать свое дело. Определенно не используйте анонимное пространство имен, если только класс не находится внутри анонимного пространства имен.

+0

Я сказал, что содержание * ключевого класса не имеет значения. Они этого не делают. Я * считал *, что контейнерный класс «вероятно» не имеет значения, но, по-видимому, он это делает. – Matthew

+0

(Извините; SO обращается с комментариями как «да, я хотел опубликовать это» ... не было сделано ...) Однако, хотя я думаю, что вы подразумеваете ответ, то, что я пытаюсь задать, должен GCC также отвергает исходный код (как это делает clang)? – Matthew

+0

@Matthew: Невозможно узнать без полного исходного кода для вашего контейнера. Если вы хотите, чтобы 'operator ==' находил контейнеры * all *, поместите его в соответствующее пространство имен. –

5

Это known GCC bug. По сути, проблема в том, что GCC неправильно выполняет поиск двухфазных имен для имен операторов. Ваш operator== не найден неквалифицированным поиском (потому что он объявлен слишком поздно) и не найден зависящим от аргумента именем поиска (потому что ::(anonymous namespace) не является ассоциированным пространством имен Bar::Foo, поэтому его не следует рассматривать внутри экземпляра qHash.

Если вы переместили определение qHash в анонимное пространство имен, GCC затем отклонит код, потому что его ошибка применима только к именам операторов, а не к обычным именам функций.

Смежные вопросы