ADL в случае одноименной функции-члена

Ситуация такова, что некоторые функции-члены bar::Bar::frobnicate хочет использовать ADL, чтобы найти функцию из некоторого неизвестного пространства имен внутри функции с одинаковым именем. Однако он находит только свое имя.

Прецедент

(Обратите внимание, что на самом деле, Bar это Foo -агностический шаблон; это просто воспроизводимый, минимальный тестовый сценарий)

namespace foo {
    struct Foo {};
    void frobnicate(Foo const &) {}
}

namespace bar {
    struct Bar {
        void frobnicate() {
            foo::Foo foo;
            frobnicate(foo); // <-- error
        }
    };
}

int main () {
    bar::Bar x;
    x.frobnicate();
    frobnicate(foo::Foo());
}

Результаты в:

test.cc: In member function ‘void bar::Bar::frobnicate()’:
test.cc:10:31: error: no matching function for call to ‘bar::Bar::frobnicate(foo::Foo&)’
test.cc:10:31: note: candidate is:
test.cc:8:18: note: void bar::Bar::frobnicate()
test.cc:8:18: note:   candidate expects 0 arguments, 1 provided

стандарт

Я понимаю, что это правильное поведение компилятора:

3.4.1 Поиск по неквалифицированному имени [basic.lookup.unqual]

(...) поиск имени заканчивается, как только найдено объявление для имени (...)

и только после неудачного поиска в игру вступает зависимый от аргумента поиск:

3.4.2 Поиск имени в зависимости от аргумента [basic.lookup.argdep]

Когда postfix-выражение в вызове функции (5.2.2) является безусловным идентификатором, можно искать другие пространства имен, не учитываемые во время обычного безусловного поиска (3.4.1)

Временное решение

Мой текущий обходной путь - ввести специальный класс черт, который не определяет само конфликтующее имя:

    struct BarTraits {
        void frobnicate_(foo::Foo const &b) {
            frobnicate(b);
        }
    };

или эта более легкая версия:

    void frobnicate_(foo::Foo const &c) { frobnicate(c); }

Вопрос

Есть ли лучшие альтернативы, чем введение таких классов черт?

Явно квалифицируя вызов как foo::frobnicate(foo) здесь не вариант, потому что (как уже упоминалось) Bar класс является шаблоном Foo в действительности, и должен работать не только для типов в foo Пространство имен.

2 ответа

Решение

Как вы сами узнали, добавив функцию-член frobnicate к интерфейсу класса Bar (или же Bar<T> в случае шаблона), не позволит ADL найти foo::frobnicate,

Самый простой - и в данном случае идиоматический- способ добавления frobnicate функциональность к классу Bar (или к шаблону класса Bar<T>) добавить функцию, не являющуюся членом frobnicate(Bar) (или шаблон функции frobnicate(Bar<T>)) в пространство имен bar

namespace foo {
    struct Foo {};
    void frobnicate(Foo const &)  {}
}

namespace bar {
    template<class T>
    struct Bar {    
       T t;    
    }; 

    template<class T>
    void frobnicate(Bar<T> const& b)
    {
        frobnicate(b.t);    
    }
}

int main () {
    bar::Bar<foo::Foo> x;
    frobnicate(x);
    frobnicate(foo::Foo());
}

Если вы настаиваете на наличии функции-члена, вам придется переименовать ее в нечто вроде do_frobnicate(), Я бы не использовал трюки с признаками типа, чтобы получить то же поведение, что и косвенный подход, и делает интерфейсы классов более сложными для понимания (помните девиз Страуструпа: "представлять свои идеи непосредственно в коде").

Вы можете использовать этот трюк

namespace dummy { void your_func(may be some parameteres); }
struct bar { 
   void member() {
      using dummy::your_func; // now your call will find that and ADL will kick in

Я просто делаю это с помощью функции прокси

namespace utils 
{
int lookup_adl(int arg) 
{
  return lookup(arg);// ADL search
}
struct Foo 
{
  int lookup(int arg) { return ::utils::lookup_adl(arg);}
};
}
Другие вопросы по тегам