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

Является ли неназначенный параметр фактически переданным во время вызова функции?

template <typename TAG>
fn(int left, TAG, int right)
{
}

fn(0, some_type_tag(), 1);
/* or */
fn(0,int(), 1); // where the primitive, int, is not empty.

РЕДАКТИРОВАТЬ: В этом вопросе есть две перспективы.

  • Объявление функции vs определение. Объявление может не указывать параметр, но объявление может сделать. Это не перспектива интереса.
  • Шаблон просматривается, в частности, в метапрограммировании. Параметр В вопросе используется тег, используемый для выведения метаструктуры из свойства. Вот почему параметр не указан, мне все равно, информация о времени компиляции - тип тега.

/EDIT

Мои теги, как правило, представляют собой пустые структуры, однако в некоторых частях моего кода они являются typedefs примитивных типов. Поэтому мне интересно узнать, действительно ли современные компиляторы передадут параметр. Это имеет два аспекта.

  • Определение размера стека с учетом размера неименованного типа параметров.
  • На самом деле создаем стек с переданным значением.

Позволяет сохранить его в gcc 4.5 и msvc 2008 +

4b9b3361

Ответ 1

На самом деле это довольно интересный вопрос.

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

Когда вы пишете вызов функции, могут произойти две вещи (в конце):

  • либо он встроен
  • или call на самом деле испускается

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

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

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

Однако, если мы говорим о пустой структуре, без метода и никакого состояния, то это просто некоторая неинициализированная память. Это не должно дорого стоить, но для этого требуется пространство в стеке (по крайней мере, резервирование).

Демо с использованием прогона llvm:

struct tag {};

inline int useless(int i, tag) { return i; }

void use(tag);

int main() {
  use(tag());
  return useless(0, tag());
}

дает:

%struct.tag = type <{ i8 }>

define i32 @main() {
entry:
  ; allocate space on the stack for `tag`
  %0 = alloca %struct.tag, align 8                ; <%struct.tag*> [#uses=2]

  ; get %0 address
  %1 = getelementptr inbounds %struct.tag* %0, i64 0, i32 0 ; <i8*> [#uses=1]

  ; 0 initialize the space used for %0
  store i8 0, i8* %1, align 8

  ; call the use function and pass %0 by value
  call void @_Z3use3tag(%struct.tag* byval %0)
  ret i32 0
}

declare void @_Z3use3tag(%struct.tag* byval)

Примечание:

  • как был удален вызов useless, и для него не создается аргумент
  • как вызвать use нельзя удалить, и поэтому пространство выделено для временного (я надеюсь, что новые версии не будут 0-инициализировать память)

Ответ 2

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

[Добавлено]

Отдельный перевод может не иметь значения для данного конкретного случая, но сборщик компилятора, который добавит такую ​​оптимизацию, должен заботиться. Они не собираются вставлять такие оптимизации, если он ломает совершенно правильный код.

Что касается шаблонов, необходимо, чтобы тип функции шаблона был равен типу функции без шаблона, иначе невозможно взять его адрес и назначить его указателю на функцию. Опять же, вы должны учитывать отдельный перевод. Просто потому, что вы не берете адрес foo<int> в этом TU, это не значит, что вы не будете в другом.

Ответ 3

Является ли параметр или нет, не влияет на подпись функции, и компилятор должен передать его. Рассмотрим, что в определении может быть назван неименованный параметр в объявлении функции.

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

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

Ответ 4

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