Необходимость заранее объявленных шаблонных функций - PullRequest
19 голосов
/ 31 августа 2011

Я недавно создал этот пример кода, чтобы проиллюстрировать использование функции шаблона C ++ 11.

template <typename Head, typename... Tail> void foo (Head, Tail...);
template <typename... Tail> void foo (int, Tail...);
void foo () {}

template <typename... Tail>
void foo (int x, Tail... tail)
{
    std :: cout << "int:" << x;
    foo (tail...);
}

template <typename Head, typename... Tail>
void foo (Head x, Tail... tail)
{
    std :: cout << " ?:" << x;
    foo (tail...);
}

foo (int (123), float (123)); // Prints "int:123 ?:123.0"

Если первые две строки, которые объявляют вперед foo, опущены, вместо этого выводится int:123int:123. Это удивило некоего опытного и знающего программиста на С ++.

Он был убежден, что предварительные декларации не должны быть необходимыми, поскольку тело не будет создано до второй фазы двухфазного поиска. Он думает, что компилятор (gcc 4.6) содержит ошибку.

Я считаю, что компилятор прав, потому что две foo являются различными функциями базового шаблона , и выбор базового шаблона должен быть заблокирован на первом этапе, иначе вы можете нарушить одно- определение правила путем создания экземпляра foo до того, как будут определены все его версии, а затем еще раз (рассмотрим, как компоновщик предполагает, что определения избыточных шаблонных функций идентичны, взаимозаменяемы и отбрасываются).

Итак, кто прав?


Приведенный выше GOTW хорошо объясняет, как и почему шаблоны функций не частично специализируются, но существование функций вариабельных шаблонов, кажется, добавляет путаницу - интуиция, что foo<int,Tail...> должна быть частичной специализацией foo<Head,Tail...> сильнее, чем эта интуиция для невариантных функций, по крайней мере, для меня.

Ответы [ 2 ]

10 голосов
/ 31 августа 2011

GCC (и Clang) правы.MSVC может ошибиться, потому что не выполняет поиск правильно.

Похоже, у вашего коллеги возникло недоразумение.Правила поиска:

  • Функция базового шаблона должна быть объявлена ​​до ее вызова из определения
  • Функция специализированного шаблона должна быть объявлена ​​до ее создания

Примечание: эти правила применяются для свободных функций, в пределах класса не требуется предварительное объявление

Обратите внимание, что, поскольку определение также действует как объявление, оно не нужнов вашем примере, чтобы объявить переадресацию версии int.

Правильный пример:

template <typename T> void foo(T);             // declare foo<T>

template <typename T> void bar(T t) { foo(t); }// call foo<T> (dependent context)

template <> void foo<int>(int);                // declare specialiaztion foo<int>

void bar(int i) { foo(i); }                    // instantiate foo<T> with int
                                               // which is the specialization

Если имеется базовый шаблон, это ошибка.Если специализация не объявлена ​​до создания экземпляра, она не будет использоваться, и это может впоследствии означать нарушение правила ODR (если другая реализация использует специализацию).

Из стандарта (C ++ 0x FDIS):

14.6.4.2

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

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

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

Если имя функции является безусловным идентификатором, и вызов был бы неправильно сформирован или нашел бы лучшее совпадение, если бы поиск в пределахв связанных пространствах имен рассматривались все объявления функций с внешними связями, введенными в эти пространства имен во всех единицах перевода, а не только те объявления, которые были найдены в определениях шаблонов и контекстах создания шаблонов, а затем программа имела неопределенное поведение.Обратите внимание, что упомянутые абзацы предназначены для обычных функций.

7 голосов
/ 31 августа 2011

Двухфазный поиск найдет:

  • функции, которые видны в точке определения, и
  • функции, которые могут быть найдены ADL в точке создания.

template <typename Head, typename... Tail> void foo (Head x, Tail... tail) не может быть найдено ADL, поэтому, если оно не видно в точке определения, оно не будет найдено вообще.

Другими словами, GCCверно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...