C++ странный неоднозначный вызов перегруженной функции [дубликат]


На этот вопрос уже есть ответ здесь:

Прежде всего, этот вопрос носит чисто теоретический характер. Я не ищу решения (я уже знаю его), я просто ищу объяснение.

Следующий код не содержит компиляция:

struct foo {};
void a(foo) {}
namespace foobar {
    void a(foo) {}
    void b(foo f) {a(f);}
}
int main() {return 1;}

MSVC++:

1>c:projectscodetestsmain.cpp(7) : error C2668: 'foobar::a' : ambiguous call to overloaded function
1>        c:projectscodetestsmain.cpp(4): could be 'void foobar::a(foo)'
1>        c:projectscodetestsmain.cpp(2): or       'void a(foo)' [found using argument-dependent lookup]
1>        while trying to match the argument list '(foo)'

G++:

main.cpp: In function 'void foobar::b(foo)':
main.cpp:5:20: error: call of overloaded 'a(foo&)' is ambiguous
main.cpp:5:20: note: candidates are:
main.cpp:4:7: note: void foobar::a(foo)
main.cpp:2:6: note: void a(foo)

В то время как этот код компилируется (MSVC++ и G++):

namespace bar {struct foo {};}
void a(bar::foo) {}
namespace foobar {
    void a(bar::foo) {}
    void b(bar::foo f) {a(f);}
}
int main() {return 1;}

Почему это? Что пространство имен вокруг foo меняет для компилятора здесь? Определяется ли это поведение в стандарте C++? Есть ли какое-то другое объяснение? Спасибо.

2 4

2 ответа:

' void a(foo)' [найдено с помощью поиска, зависящего от аргумента]

Ну, на удивление MSVC имеет очень хорошее объяснение ошибок:

Следуя стандарту , внутри функции компилятор ищет символы в текущем пространстве имен и в пространстве имен, где определен тип аргументов.

В первом случае a находится в foobar и в пространстве имен типа аргумента foo: глобальном пространстве имен, что делает его неоднозначным.

Во втором case a находится в foobar, но не в пространстве имен типа аргумента bar::foo: with is bar.

Есть два символа a(foo), и компилятор не может решить, какой из них использовать. Следовательно, вы должны явно проинструктировать компилятор.

Если вы хотите, чтобы (foo) из foobar вызывался, то попробуйте это,

   void b(foo f) { foobar::a(f); }

Если вы хотите глобальный a (foo), то попробуйте это,

   void b(foo f) { ::a(f); }