Подтвердить что ты не робот

Необходимость функции форварда-объявления шаблонов

Недавно я создал этот примерный код, чтобы проиллюстрировать использование функции вариационного шаблона С++ 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"

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

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

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

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


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

4b9b3361

Ответ 1

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 (если другой экземпляр использует специализацию).

Из стандартного (С++ 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) найдены только объявления функций, найденные либо в контексте определения шаблона, либо в контексте контекста шаблона.

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

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

Ответ 2

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

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

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

Иными словами, GCC прав.