3

У меня есть следующий код:Weird разрешения перегрузки с функцией VARIADIC шаблоны

#include <iostream> 

template <typename... Args> 
void f(int a, int b, Args... args) { 
    std::cout << b << '\n'; 
    f(a, args...); 
} 
void f(int, int b) { 
    std::cout << b << '\n';  
} 

int main() { 
    f(1, 2); 
    //f(1, 2, 3); 
} 

f(1, 2) В то время как компилирует, f(1, 2, 3) не делает. Из сообщения об ошибке, созданного компилятором, я вижу, что каким-то образом создается экземпляр f<>. Внутри экземпляра создается вызов f(a) и, следовательно, ошибка. Что заставляет компилятор не использовать f(int, int), но попытаться создать экземпляр f<>(int, int) во время разбора вызова f(1, 2, 3)?

+2

Изменить порядок объявлений. То есть поставьте свою перегрузку * сначала *, затем ваш шаблон и повторите попытку. – WhozCraig

ответ

3

В VARIADIC шаблона функции f(), то f в рекурсивном вызове является зависимым именем в связи с [temp.dep], курсив моим:

В выражении формы:

postfix-expression (expression-listopt) 

где постфикс выражения является неквалифицированным-ID, то неквалифицированных-ID де отмечает зависимое имя, если
(1,1) - любое из выражений в список_выраженияхявляется расширительным пакет (14.5.3),

И, согласно [temp.dep.res ], курсив мой:

В решении зависимых имен, имена из следующих источников считаются:
(1,1) - Заявления, которые видны на точки определения шаблона.
(1.2) - Объявления из пространств имен, связанных с типами аргументов функции как из контекста создания объектов (12.6.4.1.1), так и из контекста определения.

Существует только одна декларация f, которая видна в точке определения template <typename... Args> void f(int, int, Args...) и что само по себе. Второй пункт здесь не применяется, потому что все ваши аргументы: int s, а для основных типов нет соответствующих пространств имен. Поскольку этот шаблон функции нельзя вызывать с единственным аргументом, вы получаете ошибку компиляции.

Решение состоит в том, чтобы перестроить свой код, чтобы ваш базовый случай виден в точке определения, то есть:

// this can be just the declaration 
void f(int, int) { /* ... */ } 

template <typename... Args> 
void f(int a, int b, Args... args) 
{ 
    std::cout << b << '\n'; 
    f(a, args...); // now this will call f(int, int) 
        // if sizeof...(Args) == 1 
} 

Пример, в котором (1.2) применяется будет следующим:

#include <iostream> 

template <typename A, typename... Args> 
void f(A a, int b, Args... args) { 
    std::cout << b << '\n'; 
    f(a, args...); 
} 

template <typename A> 
void f(A a, int b) { 
    std::cout << b << '\n';  
} 

struct bar {}; 

int main() { 
    //f(1,2,3);  // still doesn't compile, same reasoning 
    f(bar{}, 2, 3); // OK. bar is in the global namespace, so declarations 
        // from the global namespace in both instantiation 
        // and definition context are considered, which includes 
        // the second `f`. 
} 
+0

Или, вперед объявить 'void f (int, int)'. – Yakk

+0

Я просто подумал, что компилятор должен сначала проанализировать прототипы, а затем определения, чтобы все объявления были видны при анализе определений. – Lingxi

+0

Другое дело, что для 'int' нет ADL. –

Смежные вопросы