Есть ли возможность иметь одну версию машинного кода для всех аргументов шаблона шаблонной функции?

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

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
}

person Pooria    schedule 20.11.2010    source источник
comment
В вашем примере по значению, вероятно, невозможно сложить разные экземпляры вместе, потому что размер параметра elements будет другим. Это, в свою очередь, повлияет на пролог/эпилог функции (где стек выделяется, а затем очищается).   -  person Crashworks    schedule 20.11.2010
comment
@Crashworks_Значит, выделение стека не может выполнять работу по резервированию разных размеров фрейма стека для каждого вызова функции?   -  person Pooria    schedule 20.11.2010
comment
Вы должны попробовать просмотреть пару вызовов функций в окне дизассемблирования вашего отладчика и посмотреть, как на самом деле распределяется стек. Это, вероятно, было бы более поучительным, чем все, что я мог бы поместить в эту маленькую коробку. знак равно   -  person Crashworks    schedule 20.11.2010


Ответы (3)


Интеллектуальный компоновщик может распознать, когда два разных тела функций идентичны, и объединить их вместе в один символ. Это называется «свертывание 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() {};
}

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

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

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

person Crashworks    schedule 20.11.2010

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

person Puppy    schedule 20.11.2010

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

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

person Ben Jackson    schedule 20.11.2010
comment
В этом случае он не может быть параметром конструктора, потому что он должен быть константой времени компиляции. - person UncleBens; 20.11.2010
comment
Если вы говорите так из-за SomeType elements[num], что вам мешает перейти на SomeType *elements и динамическое размещение? В конце концов, std::vector умудряется быть очень похожим на массив, не используя размер в качестве параметра шаблона. - person Ben Jackson; 20.11.2010