Какой компилятор подходит для следующего поведения при перегрузке / специализации?
-
03-07-2019 - |
Вопрос
Рассмотрим следующий код:
#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();
}
Каков ожидаемый результат?"T" или 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 стандарта говорится, что если имя функции является неквалифицированный-id но не шаблон-идентификатор затем для поиска без ADL рассматриваются только функции, видимые в точке определения шаблона.Нет никаких аргументов функции из Foo
пространство имен, поэтому поиск, зависящий от аргумента, не выполняется, следовательно, версия шаблона foo
вызывается, а не перегрузка без шаблона.
Большое спасибо litb за исправления к этому ответу.
Если вы сделали это специализацией, как показано ниже, то, поскольку специализации выбираются во время создания экземпляра шаблона, специализацию можно вызывать до тех пор, пока соответствующая специализация видна в точке, для которой шаблон функции создается впервые int
.
namespace Foo {
template<>
void foo<int>(int *, int) { puts("int"); }
}
Глава 14 текущего стандарта, но она не очень читабельна :)
Редактировать:Если бы мне пришлось выбирать наиболее релевантную часть стандарта, это, вероятно, был бы пункт 9 статьи 14.6 [temp.res].(Слегка сокращенный) Если имя не зависит от шаблон-параметр, объявление для этого имени должно находиться в области видимости в том месте , где это имя появляется в определении шаблона;имя привязано к объявлению, найденному в этой точке, и на это привязывание не влияют объявления, которые видны в момент создания экземпляра.
Править, править:Но вам также необходимо принять во внимание 14.6.4.2 [временный деп.кандидат].Пытаться ссылаться на стандарт очень сложно и опасно из-за всех взаимозависимостей, и этот ответ является тому примером.
Другие советы
Как правило, из двух версий компилятора более поздняя, скорее всего, будет более стандартной.