Почему этот код C ++, связанный с перегрузкой / пространством имен / шаблоном, не компилируется? - PullRequest
12 голосов
/ 03 декабря 2011

Вот код C ++:

namespace A {

int f(int x) { return 0; }
int f(long x) { return 1; }

template<class T> int g(T x) {
  return f(x);
}

}

namespace B {
struct C {};
}

namespace A {
int f(B::C x) { return 2; }
}

void h() {
  A::g(B::C());
}

В пространстве имен A код объявляет несколько перегрузок функции f и шаблонной функции g, которая вызывает f. Затем мы объявляем новый тип в пространстве имен B и перегружаем f для нового типа в пространстве имен A . Компиляция с g ++ 4.2 дает

order.cpp: In function ‘int A::g(T) [with T = B::C]’:
order.cpp:21:   instantiated from here
order.cpp:7: error: no matching function for call to ‘f(B::C&)’
order.cpp:3: note: candidates are: int A::f(int)
order.cpp:4: note:                 int A::f(long int)

Код работает, если я выполню одно из следующих действий:

  1. Удалить пространства имен.
  2. Переместить перегрузку f для B :: C в пространство имен B (благодаря поиску Кенига).
  3. Переместите объявление B :: C и его перегрузку f выше определения g ().

Я особенно озадачен (3), поскольку у меня сложилось впечатление, что разрешение перегрузки должно быть независимым от порядка объявлений. Это ожидаемое поведение C ++?

Ответы [ 2 ]

6 голосов
/ 15 декабря 2011

Clang выдает следующее сообщение об ошибке, которое дает некоторые подсказки к проблеме:

$ clang -fsyntax-only test.cc -Wall
test.cc:7:10: error: call to function 'f' that is neither visible in the
      template definition nor found by argument-dependent lookup
  return f(x);
         ^
test.cc:21:3: note: in instantiation of function template specialization
      'A::g<B::C>' requested here
  A::g(B::C());
  ^
test.cc:17:5: note: 'f' should be declared prior to the call site or in
      namespace 'B'
int f(B::C x) { return 2; }
    ^
1 error generated.

В частности, вы столкнулись с подробностями двухфазного поиска зависимых имен в определениях шаблонов. В C ++ 98 [temp.dep.candidate] говорит:

Для вызова функции, который зависит от параметра шаблона, если имя функции является неквалифицированным идентификатором, но не идентификатором шаблона, функции-кандидаты находятся с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением что:

  • Для части поиска с использованием поиска без определения имени (3.4.1) найдены только объявления функций с внешней связью из контекста определения шаблона.
  • Для части поиска, использующей связанные пространства имен (3.4.2), найдены только объявления функций с внешней связью, найденные либо в контексте определения шаблона, либо в контексте создания шаблона.

Так как A::f(B::C x) не найден с использованием связанных пространств имен (то есть поиск, зависящий от аргумента), он должен быть виден на сайте определения шаблона, а не только в момент его создания.

0 голосов
/ 03 декабря 2011

Например

int f(int x) { return 0; }
int f(long x) { return 1; }

функции не являются шаблонными функциями (т.е. они не имеют template <class T> перед ними. T является параметром шаблона.) Поэтому они могут быть скомпилированы на лету, когда достигнут шаблонный код.

...