Разрешение имен функций внутри шаблонов, созданных с помощью квалифицированных типов

Рассмотрим следующий пример кода C ++:

namespace n
{
struct A {};
}

struct B {};

void foo(int) {}

template<typename T>
void quux()
{
foo(T());
}

void foo(n::A) {}
void foo(B) {}int main()
{
quux<n::A>(); // Error (but works if you comment out the foo(int) declaration)
quux<B>();    // Works

return 0;
}

Как указано в комментарии, шаблон экземпляра quux<n::A>() вызывает ошибку компилятора (на GCC 4.6.3):

foo.cpp: In function ‘void quux() [with T = n::A]’:
foo.cpp:22:16:   instantiated from here
foo.cpp:13:5: error: cannot convert ‘n::A’ to ‘int’ for argument ‘1’ to ‘void foo(int)’

Может кто-нибудь объяснить мне, что происходит? Я бы ожидал, что он будет работать так же, как с quux<B>(), Это должно иметь отношение к тому, когда foo считается зависимым. К сожалению, мой C ++ foo недостаточно хорош. Пример компилируется нормально, когда foo(int) декларации нет, что тоже меня удивляет.

Любые советы, объяснения и обходные пути приветствуются.

Обновление 1:

Я не хочу (читать не могу) переместить декларацию foo(n::A) до определения quux (что позволит избежать ошибки).

Обновление 2:

Спасибо за Дэвида за то, что он указал на связанный вопрос Вызов функции шаблона сбит с толку функцией с неверной подписью, объявленной перед шаблоном. Принятый ответ Йоханнеса Шауба — Литба предлагает решение класса обертки, которое также будет работать в моем случае в качестве обходного пути. Однако я не на 100% доволен этим.

Обновление 3:

Я решил проблему, поставив определение foo(n::A) в пространстве имен n, Спасибо за полезные ответы Джесси Гуд и bames53, которые не только указывают на соответствующие разделы стандарта, но и предоставляют альтернативные решения. Спасибо Дэвиду Родригесу — Дрибеасу за его объяснение, когда я не совсем правильно понял предложенные решения, и всем другим участникам.

7

Решение

Я думаю, что правило 14.6.4.2p1:

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

— Только для части поиска с использованием поиска без определения имени (3.4.1) или поиска с квалифицированным именем (3.4.3)
Найдены объявления функций из контекста определения шаблона.

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

void foo(n::A) {} является невидимый в контексте определения шаблона, потому что он идет после и foo не находится в том же пространстве имен, что и n::A, Так что это должно быть либо видимым до определение шаблона или включены в то же пространство имен, как показано ниже:

namespace n
{
void foo(n::A) {}
}
2

Другие решения

Ошибка моего компилятора:

main.cpp:11:5: error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup
foo(T());
^
main.cpp:18:5: note: in instantiation of function template specialization 'quux<n::A>' requested here
quux<n::A>(); // Error (but works if you comment out the foo(int) declaration)
^
main.cpp:14:6: note: 'foo' should be declared prior to the call site or in namespace 'n'
void foo(n::A) {}
^

Что дает понять, в чем проблема.

Комментируя void foo(int) не заставьте это работать однако; это просто ошибка / расширение в вашем компиляторе.

Вы упоминаете, что не можете определить void foo(n::A) до quux()Однако, когда вы говорите в комментариях, что вы не можете определить его внутри пространства имен nПричины, которые вы приводите, похоже, не применяются. Это должно решить проблему без других упомянутых вами проблем.

template<typename T>
void quux()
{
foo(T());
}

namespace n {
void foo(n::A) {}
}
using n::foo; // in case there's any other code that depends on getting foo(n::A) from the global namespace

void foo(B) {} // this stays in the global namespace

Если вы не можете переместить определение void foo(n::A) где он работает с правильным двухфазным поиском (опять же, либо до quux() или же внутри пространства имен n) есть своего рода хакерское решение, которое может работать на вас: forward объявляет правильную перегрузку foo() внутри quux(),

template<typename T>
void quux()
{
void foo(T);
foo(T());
}

Функция в конечном итоге должна быть определена в том же пространстве имен, что и quux() и оно должно соответствовать «родовой» предварительной декларации.


Или есть другая альтернатива. Это было довольно недавно, что большинство компиляторов C ++ начали предлагать правильный двухфазный поиск имен, так что есть много кода, который не верен, но который компиляторы хотят поддерживать. Если вы не можете изменить свой код, тогда он может быть хорошим кандидатом для включения опции компилятора совместимости; мой компилятор принимает флаг -fdelayed-template-parsing отключить двухфазный поиск имен и вместо этого всегда искать имена в контексте реализации.

1

По вопросам рекламы [email protected]