Поиск в зависимости от аргумента в базе класса шаблона

У меня есть класс-шаблон NB::B<T>, производный от класса-шаблона NA::A в пространстве имен. act<T> — это функция шаблона, вызывающая функцию add_ref для экземпляра своего аргумента шаблона. В частности, act<NB::B<int>> хочет найти add_ref, определенное в пространстве имен базы NB::B, используя ADL. Полный пример следующий:

template<class T>
void act() {
  T* p = 0;
  add_ref(p); // the failing line
}

namespace NA
{
  struct A { };

  // I want ADL to find this:
  void add_ref(A* p) {
  }
}

namespace NB
{
  // template class with non-template base
  template <class T>
  struct B: NA::A { };

  typedef B<int> Bi;

  // using NA::add_ref; // fixes the problem
}

int main()
{
  act<NB::Bi>();
}

Это нормально компилируется в gcc (4.7.0). И в Comeau онлайн. Однако clang (3.1) терпит неудачу:

a.cpp:4:3: error: use of undeclared identifier 'add_ref'

При этом стандарт гласит:

3.4.2/2 …

— Если T является идентификатором шаблона, связанные с ним пространства имен и классы являются пространством имен, в котором определен шаблон; для шаблонов-членов — класс шаблона-члена; пространства имен и классы, связанные с типами аргументов шаблона, предоставленными для параметров типа шаблона (исключая параметры шаблона шаблона); пространства имен, в которых определены любые аргументы шаблона шаблона; и классы, в которых определены любые шаблоны элементов, используемые в качестве аргументов шаблона шаблона.

Удивительно, но базы шаблонов не указаны как пути к связанным пространствам имен. Таким образом, поведение clang кажется правильным. А Comeau и gcc принимают неверную программу.

В то же время 3.4.2/3 утверждает, что using в пространствах имен аргументов не действуют:

При рассмотрении связанного пространства имен поиск аналогичен поиску, выполняемому, когда связанное пространство имен используется в качестве квалификатора (3.4.3.2), за исключением того, что:

— Любые директивы использования в связанном пространстве имен игнорируются.

Но когда я раскомментирую строку using NA::add_ref, clang с радостью скомпилирует тест.

Чтобы представить мой пример в практической перспективе, вы можете подумать, что act был методом boost::intrusive_ptr, add_ref(A*) был intrusive_ptr_add_ref(CBase*), а B был неким шаблоном, производным от базы CBase.

В связи с этим у меня несколько вопросов:

  1. Прав ли я в том, что clang правильно отвергают мою тестовую программу, а gcc и Comeau не соответствуют стандарту?

  2. Есть ли причина, по которой стандарт определяет такое непрактичное поведение (запрещает базы классов шаблонов в качестве связанных пространств имен)?

  3. Является ли clang неправильным принятие моей тестовой программы с директивой using NA::add_ref на основании 3.4.2/3?

  4. Должен ли я сообщить об ошибке? :)

P.S. Я прочитал часто задаваемые вопросы о совместимости языков и не нашел там ответа.


person Nicht Verstehen    schedule 23.07.2012    source источник
comment
Помогает ли режим С++ 11? С++ 11, похоже, уточнил формулировку, поскольку на самом деле NB::B<int> является классом (который оказывается специализацией шаблона), а не шаблоном. (Правила IIUC заключаются в том, что пространства имен, связанные с шаблоном, специализацией которого является тип, добавляются к пространствам имен, связанным с классом.)   -  person Luc Danton    schedule 23.07.2012
comment
Не будет ли это очень хрупким? В какой-то момент вы, вероятно, добавите реализацию add_ref по умолчанию, которая всегда будет лучше соответствовать, чем связанное пространство имен прямой базы?   -  person pmr    schedule 23.07.2012
comment
@LucDanton, ты прав. Оба пункта (о пространствах имен, связанных с class и template-id) относятся к классам template-id.   -  person Nicht Verstehen    schedule 24.07.2012
comment
@pmr, это действительно довольно хрупко. Хотя я не понял вашего комментария. Без ADL ничего не будет найдено, потому что никакие add_ref функции не находятся в области вызова из intrusive_ptr.hpp.   -  person Nicht Verstehen    schedule 24.07.2012
comment
@NichtVerstehen Под реализацией по умолчанию я имел в виду: если только в области действия вашей функции act нет функции add_ref. Шаблон в пространстве имен act() может лучше соответствовать и будет выбран вместо функции, связанной с базой.   -  person pmr    schedule 24.07.2012
comment
Что касается пункта 3, using NA::add_ref; не является директивой использования, это декларация использования, поэтому 3.4.2/3 не применяется. Директива использования выглядит как using namespace NA;.   -  person Richard Smith    schedule 31.07.2012


Ответы (1)


Из n3337, который в основном представляет собой C++11 с небольшими редакционными изменениями, 3.4.2/2 гласит:

Для каждого аргумента типа T в вызове функции [...] Наборы пространств имен и классов определяются следующим образом: [...]

  • Если T является типом класса (включая объединения), его ассоциированными классами являются: сам класс; класс, членом которого он является, если таковой имеется; и его прямые и косвенные базовые классы. Связанные с ним пространства имен — это пространства имен, членами которых являются связанные с ним классы. Кроме того, если T является специализацией шаблона класса, ...

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

  1. Gcc и comeau правы, а clang++ ошибается, отказываясь от кода.

  2. ‹ не применяется >

  3. Clang++ ошибается, отвергая его без using NA::add_ref.

  4. Да, вероятно, вам следует сообщить об ошибке. Кажется, о ней уже сообщалось и она была исправлена.

person David Rodríguez - dribeas    schedule 23.07.2012
comment
об ошибке уже сообщалось некоторое время назад, и она была исправлена. ошибка zhr заключалась в том, что tag clang не расшифровывал шаблон, чтобы найти его базовые классы (и его объявления друзей) - person Johannes Schaub - litb; 23.07.2012
comment
Спасибо, @david-rodriguez-dribeas. Это «более того» проясняет вопрос. - person Nicht Verstehen; 24.07.2012
comment
@johannes-schaub-litb, Вы случайно не помните ссылку на ошибку, о которой говорите? Я только что проверил это на новой сборке clang транка, и ошибка все еще присутствует. - person Nicht Verstehen; 24.07.2012