Рассмотрим следующий код:
#include <stdio.h>
namespace Foo {
template <typename T>
void foo(T *, int) { puts("T"); }
template <typename T>
struct foo_fun {
static void fun() { foo((T *)0, 0); };
};
}
namespace Foo {
void foo(int *, int) { puts("int"); }
}
using namespace Foo;
int main() {
foo_fun<int> fun;
fun.fun();
}
Какой ожидаемый результат? «Т» или int?
Один компилятор (gcc 4.0.1 от Apple Xcode 3.1.2) выводит "int", два других компилятора (gcc 4.1.2 и 4.1.3) выводят "T".
Если я перемещаю объявление / определение foo (int *, int) перед версией foo (T *, int), все выводится как «int». Определен ли в данном случае порядок перегрузки / специализации действующим стандартом?





Второй void foo(... - это перегрузка (а не специализация), которая не видна в определении foo_fun::fun, поэтому не будет найдена в контексте определения шаблона. Поскольку T* является зависимым типом, разрешение foo в выражении foo((T*)0, 0) будет отложено до времени создания экземпляра шаблона, а также будет учитываться контекст создания экземпляра. Однако в 14.6.4.2 стандарта сказано, что если имя функции - неквалифицированный идентификатор, но не идентификатор шаблона, то для поиска, не относящегося к ADL, рассматриваются только функции, видимые в точке определения шаблона. Нет аргументов функции из пространства имен Foo, поэтому поиск, зависящий от аргументов, не происходит, поэтому вызывается версия шаблона foo, а не перегрузка, не являющаяся шаблоном.
Большое спасибо litb за исправления этого ответа.
Если вы сделали это специализацией, как показано ниже, то, поскольку специализации выбираются во время создания экземпляра шаблона, специализацию можно вызывать до тех пор, пока соответствующая специализация видна в точке, в которой шаблон функции впервые создается для int.
namespace Foo {
template<>
void foo<int>(int *, int) { puts("int"); }
}
Глава 14 действующего стандарта, но она не очень удобочитаема :)
Обновлено: если бы мне пришлось выбрать наиболее подходящую часть стандарта, это, вероятно, было бы 14.6 [temp.res] параграф 9. (Немного сокращено) Если имя не зависит от параметр-шаблон, объявление для этого имени должно быть в области в том месте, где имя появляется в определении шаблона; имя привязано к объявлению, найденному в этот момент, и на эту привязку не влияют объявления, видимые в момент создания экземпляра.
Редактировать, редактировать: Но вы также должны принять во внимание 14.6.4.2 [temp.dep.candidate]. Очень сложно и опасно пытаться ссылаться на стандарт из-за всех взаимозависимостей, этот ответ является показательным.
Я бы рискнул и сказал, что да.
Я обновил код, используя пространство имен Foo (разница от компиляторов все еще есть). Объяснение контекста создания экземпляра звучит неубедительно.
Обновился снова, мы доберемся до конца!
Да, здесь главное - 14.6.4.2.
да, ваш ответ действительно правильный. Я обнаружил сейчас (что я уже подозревал, но не был уверен), что пример в 14.6 / 9 ошибочен (см. open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197). только ADL происходит в точке интереса. удалил свой ответ, потому что он был основан на том некорректном примере
также см. groups.google.com/group/comp.lang.c++.moderated/browse_threa d /… для полезного обсуждения этого
и действительно, текущий черновик n2800 уже содержит исправление. ура! :)
Как правило, из двух версий компилятора последняя, скорее всего, будет более стандартной.
В целом это правильно. Однако gcc от Apple включает в себя собственные исправления, которые вышли позже (с Xcode 3.1.2), чем gcc 4.1.2 / 3.
Спасибо! в этом есть смысл. То есть вы говорите, что g ++ 4.0.1 от Apple не соответствует стандартам?