создание шаблона на c ++ - PullRequest
       20

создание шаблона на c ++

0 голосов
/ 08 декабря 2008

У меня есть шаблон класса, как показано ниже.

template<int S> class A
{
private:
  char string[S];

public:
  A()
  {
    for(int i =0; i<S; i++)
    {
      .
      .
    }
  }

  int MaxLength()
  {
    return S;
  }
};

Если я создаю экземпляр вышеупомянутого класса с разными значениями S, будет ли компилятор создавать разные экземпляры функций A () и MaxLenth ()? Или он создаст один экземпляр и передаст S как своего рода аргумент?

Как это будет вести себя, если я перенесу определение A и Maxlength в другой файл cpp.

Ответы [ 7 ]

5 голосов
/ 08 декабря 2008

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

Наиболее вероятно, что компилятор включит по крайней мере MaxLength (), но, возможно, и ваш ctor. В противном случае он вполне может сгенерировать один экземпляр вашего ctor и передать / заставить его извлечь S из другого места. Единственный способ узнать наверняка - это проверить вывод компилятора.

Итак, чтобы знать наверняка, я решил перечислить, что VS2005 делает в сборке релиза. Скомпилированный файл выглядит следующим образом:

template <int S>
class A
{
  char s_[S];
public:
  A()
  {
    for(int i = 0; i < S; ++i)
    {
      s_[i] = 'A';
    }
  }
  int MaxLength() const
  {
    return S;
  }
};

extern void useA(A<5> &a, int n); // to fool the optimizer
extern void useA(A<25> &a, int n);

void test()
{
  A<5> a5;
  useA(a5, a5.MaxLength());
  A<25> a25;
  useA(a25, a25.MaxLength());
}

Вывод ассемблера следующий:

?test@@YAXXZ PROC                   ; test, COMDAT

[snip]

; 25   :    A<5> a5;

mov eax, 1094795585             ; 41414141H
mov DWORD PTR _a5$[esp+40], eax
mov BYTE PTR _a5$[esp+44], al

; 26   :    useA(a5, a5.MaxLength());

lea eax, DWORD PTR _a5$[esp+40]
push    5
push    eax
call    ?useA@@YAXAAV?$A@$04@@H@Z       ; useA

Как видите, как ctor, так и вызов MaxLength () встроены. И как вы теперь можете догадаться, он делает то же самое с типом A <25>:

; 28   :    A<25> a25;

mov eax, 1094795585             ; 41414141H

; 29   :    useA(a25, a25.MaxLength());

lea ecx, DWORD PTR _a25$[esp+48]
push    25                  ; 00000019H
push    ecx
mov DWORD PTR _a25$[esp+56], eax
mov DWORD PTR _a25$[esp+60], eax
mov DWORD PTR _a25$[esp+64], eax
mov DWORD PTR _a25$[esp+68], eax
mov DWORD PTR _a25$[esp+72], eax
mov DWORD PTR _a25$[esp+76], eax
mov BYTE PTR _a25$[esp+80], al
call    ?useA@@YAXAAV?$A@$0BJ@@@H@Z     ; useA

Очень интересно посмотреть, как хитрый компилятор оптимизирует цикл for. Для всех этих преждевременных оптимизаторов, использующих memset (), я бы сказал, что вы дурак.

Как это будет вести себя, если я перенесу определение A и Maxlength в другой файл cpp.

Вероятно, он не скомпилируется (если только вы не используете A в этом cpp-файле).

5 голосов
/ 08 декабря 2008

Шаблон будет создан для каждого отдельного значения S.

Если вы переместите реализации метода в другой файл, вам нужно #include этот файл. (Например, Boost использует соглашение .ipp для таких исходных файлов, которые должны быть #included).

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

2 голосов
/ 08 декабря 2008

Если я создаю экземпляр вышеупомянутого класса с различные значения S, будет компилятор создает разные экземпляры Функция A () и MaxLenth ()? Или будет это создать один экземпляр и передать S как своего рода аргумент?

Компилятор создает отдельную копию шаблона класса для каждого отдельного значения параметра. Что касается функций-членов, то для каждого отдельного значения S будет создана отдельная копия каждой из них. Но в отличие от функций-членов не шаблонных классов, они будут генерироваться только в том случае, если они действительно используются.

Как он будет себя вести, если переместить определение A и Maxlength в другой файл cpp.

Вы имеете в виду, если вы поместите определение A в заголовочный файл, но определите функцию-член MaxLength в файле cpp? Хорошо, если пользователи вашего шаблона класса хотят вызвать MaxLength, компилятор хочет увидеть его код, так как он хочет создать его копию с фактическим значением S. Если у него нет доступного кода, он предполагает, что код предоставлен иначе, и не генерирует никакого кода:

A.hpp

template<int S> class A {
public:
    A() { /* .... */ }
    int MaxLength(); /* not defined here in the header file */
};

a.cpp

template<int S> int
A<S>::MaxLength() { /* ... */ }

Если теперь включить только код A.hpp для кода, использующего шаблон класса A, то компилятор не увидит код для MaxLength и не будет создавать никаких экземпляров. У вас есть два варианта:

  • Включите файл A.cpp , чтобы компилятор видел код для него, или
  • Предоставьте явную реализацию значений S, которые, как вы знаете, будут использоваться. Для этих значений вам не нужно указывать код MaxLength

Для второго варианта это делается путем помещения строки, подобной следующей, внутри A.cpp :

template class A<25>;

Теперь компилятор сможет выжить, не увидев код для функций-членов A<25>, поскольку вы явно создали копию своего шаблона для S=25. Если вы не выполните ни одну из двух указанных выше опций, компоновщик откажется создать конечный исполняемый файл, поскольку по-прежнему отсутствует код, который необходим.

1 голос
/ 08 декабря 2008

A<S>::MaxLength() настолько тривиально, что будет полностью встроено. Следовательно, будет 0 копий. A<S>::A() выглядит более сложным, поэтому, скорее всего, будет создано несколько копий. Разумеется, компилятор может решить не делать этого, если код работает так, как задумано.

Возможно, вы захотите посмотреть, можете ли вы переместить цикл в A_base :: A_base (int S).

1 голос
/ 08 декабря 2008

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

0 голосов
/ 08 декабря 2008

Компилятор создаст разные экземпляры класса, если вы создадите его для разных типов или параметров.

0 голосов
/ 08 декабря 2008

Будет создано две разные версии A() и MaxLength(), которые будут возвращать константы времени компиляции. Простое return S; будет эффективно скомпилировано и даже встроено, где это возможно.

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