2010-11-20 1 views
5

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

template<size_t num>  
struct Elements{ 
public: 
    SomeType elements[num]; 
}; 

template<size_t num> 
void print(const Elements<num> & elements,size_t size){ 
//all instances do exactly same thing and with regard to Size that determines the size of object 
} 

и, конечно, есть передача по версии значения также:

template<size_t num> 
void print(const Elements<num> elements,size_t size){ 
//all instances do exactly same thing and with regard to Size that determines the size of object 
} 
+0

В приведенном ниже примере, вероятно, невозможно свернуть разные экземпляры вместе, поскольку размер параметра элементов будет другим. Это, в свою очередь, повлияет на функцию prolog/epilog (где стек распределяется, а затем очищается). – Crashworks

+0

@Crashworks_So распределение стека не может выполнять задание резервирования различных размеров кадра стека за каждый вызов функции? – Pooria

+0

Вы должны попробовать посмотреть несколько вызовов функций в окне разборки отладчика и посмотреть, как фактически распределяется стек. Это, вероятно, было бы более просвещенным, чем все, что я мог вписаться в эту маленькую коробку. =) – Crashworks

ответ

5

Смарт-линкер может распознать, когда две различные функции тела идентичны и объединить их в один символ. Это называется «Складывание COMDAT» в MSVC и «дублирование снятия» в большинстве других мест. Например, следующие две функции могут быть скомпилированы идентично на PPC, хотя они принимают разные типы, потому что типы имеют одинаковый размер и ведут себя одинаково в данной ситуации.

template<typename T> 
GetLowBit(T foo) { return T & 1; } 

GetLowBit<unsigned long>(ulong x); // compiles to "li r4, 1 ; and r3, r3, r4 ; blr " 
GetLowBit<signed long>(long x); // also compiles to "li r4, 1 ; and r3, r3, r4 ; blr " 

и поэтому компоновщик может сделать и их «имена» указывают на то же место, как это было, так что вызов GetLowBit<unsigned long> идет по тому же адресу, как призыв к GetLowBit<signed long>. Таким образом, в общем, можно скомпоновать различные шаблонные экземпляры функций, которые одинаково работают на одинаковых типоразмерах. (В частности, контейнеры, в которых хранятся указатели или перечисления, как правило, складываются вместе.)

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

class A 
{ 
    virtual void nothing() {}; 
} 

class B 
{ 
    virtual void empty() {}; 
} 

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

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

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

0

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

0

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

Более общо: Если вы обнаружили, обеспокоено тем, что вы могли бы сделать много специализированных экземпляров шаблона из-за параметр (ваш size_t num является прекрасным примером) вы, вероятно, обнаружили параметр шаблона, который должен действительно стать конструктором параметр.

+0

В этом случае он не может быть параметром конструктора, поскольку он должен быть константой времени компиляции. – UncleBens

+0

Если вы говорите, что из-за 'SomeType elements [num]', что мешает вам перейти к 'SomeType * elements' и динамическому распределению? В конце концов, 'std :: vector' управляется очень массивно, не принимая размер в качестве параметра шаблона. –

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

  • Нет связанных вопросов^_^