Почему шаблоны не могут быть объявлены в функции?

#c #templates #inner-classes

#c #шаблоны

Вопрос:

Чтение шаблонов C : полное руководство, и в нем говорится

Обратите внимание, что шаблоны не могут быть объявлены в функции

Это не дает объяснения и / или перекрестной ссылки на какую-либо другую главу в книге или внешний ресурс.

Не мог бы кто-нибудь помочь в объяснении этого. Возможно, это объясняется позже в книге, но пока не там. Если это объяснялось ранее, я, должно быть, пропустил это.

Пример:

 int main()
{
  class DummyClass  //  This compiles ok
  {
    int object;
  };

  template <typename T> //  compile error "expected primary-expression before "template""
  class DummyTemplate
  {
    T object;
  };

  return 0;
}
  

Я также не понимаю сообщение об ошибке от gcc. Сообщение об ошибке гласит:

 expected primary-expression before "template"
  

Комментарии:

1. Есть несколько ответов, которые в основном говорят «вы не можете, потому что вы не можете». Кто-нибудь знает, есть ли веская причина не делать этого?

2. Аргументы шаблона должны иметь внешнюю привязку. Относительно того, почему это необходимо, есть несколько советов, предоставленных Грегом Комо в этом обсуждении c.l.c .moderated @ groups.google.com/group/comp.lang.c .moderated/browse_thread /…

3. @Abhay: вот почему классы в области действия функции не могут быть аргументами шаблона, а не почему объявления шаблонов не могут быть в области действия функции.

4. @Mike Seymour: Если вы знаете точную причину, почему бы вам просто не просветить нас 🙂

5. @Tony: Шаблоном может быть какой-либо функциональный объект, который должен использоваться только внутри этой одной функции. Я бы хотел, чтобы я мог сделать их локальными.

Ответ №1:

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

Комментарии:

1. Это должно быть принятым ответом, поскольку он, по крайней мере, пытался обосновать это, а не просто «потому что так говорит стандарт».

Ответ №2:

Ответ «потому что так говорит стандарт», конечно, правильный, но давайте рассмотрим общие лямбды.

В C 14 и C 17 универсальные лямбды — единственный известный мне способ написания шаблоноподобного кода:

     auto lambda = [](auto x) { };
    lambda.operator()<int>(0);
  

Технически, вы можете написать любой шаблонный код только с этим. Хотя вам придется потрудиться, чтобы обойти различные ограничения этого подхода.

Хотя в C 20 это будет проще. Используя список параметров шаблона в универсальных лямбда-выражениях, вы сможете написать код, подобный этому:

     auto size = []<class T>() { return sizeof(T); };
    static_assert(4 == size.operator()<int>());
  

GCC уже поддерживает этот синтаксис.

Комментарии:

1. Интересно. Можете ли вы частично специализировать общие лямбды?

2. @ceztko вы не можете частично специализировать шаблонные функции или методы (в случае универсального лямбда-выражения у нас есть шаблонный метод operator() ). Однако вы можете полностью специализировать лямбда-выражения operator() (вот пример godbolt.org/z/dspRsA ), но вы, вероятно, найдете if constexpr и std::is_same_v более полезные.

Ответ №3:

Короткий ответ на вопрос, почему это так, заключается в том, что именно так хотели ребята, которые писали компиляторы и стандарты c / c . Шаблоны внутри функций, должно быть, были сочтены слишком хаотичными и / или сложными для понимания или анализа, поэтому они запретили это.

Комментарии:

1. Спасибо. Очень понятно, и я вижу, что в этом есть большой смысл. Какие-либо ссылки?

2. @MeThinks: Смотрите мой комментарий к вашему комментарию.

Ответ №4:

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

Комментарии:

1. Это уже реализовано начиная с C 14 в универсальных лямбда-выражениях: объект [](auto x) {} имеет template operator() , у него просто нет именованных параметров шаблона.

Ответ №5:

Это означает, что вы не можете сделать что-то вроде следующего

   void foo()
  {
       template <typename T> //Error
       T something;
  }
  

Объявления шаблонов разрешены только в глобальной области, пространстве имен или классе. 🙂

В чем причина этого?

Это не разрешено, потому что так сказано в стандарте.

ISO C -98 (раздел 14.2)

Объявление шаблона может отображаться только как объявление пространства имен или области видимости класса.

Имеет ли это смысл?

Комментарии:

1. ДА. Спасибо. Вопрос в том, почему это так. В чем причина этого?

2. What is the reasoning behind it? . Я не знаю много технических деталей относительно реализации объявления шаблона внутри функции, но это не разрешено стандартом. В ISO C (14.2) говорится A *template declaration* can appear only as a namespace or class scope declaration 🙂

3. 1. Спасибо. Да, это имеет смысл. Я вижу, стандарт этого не допускает. Хотя это помогло бы понять, почему. Я видел вопросы о том, что стандарты разрешают (не), на которые были даны ответы с некоторыми пояснениями в поддержку стандарта. Очень полезно для таких учащихся, как я. Это никоим образом не отменяет ничего из этого очень хорошего ответа.

4. Что ж, я был бы одним из тех, кто также не рекомендует объявлять структуры / функции внутри функции. Я всегда считал, что это больше всего портит код. В любом случае, я полагаю, это помогает облегчить жизнь компилятору-автору, и у них достаточно сложная работа, поскольку не требуется такого рода «функция», которая мало что привносит в таблицу.

5. «Это не разрешено, потому что это недопустимо» — это неверный ответ на вопрос «Почему?» Также логическая ошибка.

Ответ №6:

Какая именно от этого была бы польза? Таким образом, вы можете объявлять переменные шаблона, которые вы можете использовать только внутри функции? Это действительно полезно?

Комментарии:

1. ДА. Я могу сделать это со структурами и классами. Единственное, о чем я могу думать, это объекты функций относительно классов / структур для использования с алгоритмами внутри функции. Не требуется, но я делаю это, чтобы классы / структуры были близки к тому месту, где я их использую. Предположительно, у меня могла быть какая-то причина, но это скорее учебное упражнение. Тот факт, что это может быть бесполезно, я думаю, это другое обсуждение.

2. Его полезность на самом деле не является еще одним обсуждением, ИМО, это очень уместно для этого. Если бы это действительно было разрешено, как бы вы это использовали? Внутренности функции не видны вызывающей стороне, так как же вы создадите экземпляр параметра шаблона с соответствующим типом при вызове этой функции?

3. @Preatirian. Я слышу тебя. Однако мне может не обязательно передавать аргументы шаблона. Я могу использовать шаблон несколько раз, с разными аргументами шаблона, в функции. Я просто громко думаю здесь, чтобы узнать 🙂

4. @Praetorian Извините за орфографическую ошибку в имени

5. @Praetorian: В C 03 вы не можете создавать экземпляры шаблонов с локальными типами. Однако вы могли бы использовать экземпляры этого шаблона с другими шаблонами. (Например, он может быть производным от полиморфного базового класса, и вы могли бы использовать экземпляры для вызова функций, принимающих ссылки / указатели на этот базовый класс.)

Ответ №7:

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

Комментарии:

1. Тем не менее, я согласен, что это странное несоответствие в языке. Как говорят все остальные, просто запишите это следующим образом: «потому что так оно и есть».