Фактически передается неназванный параметр во время вызова функции? - PullRequest
17 голосов
/ 20 мая 2011
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.

РЕДАКТИРОВАТЬ: Есть два взгляда на этот вопрос.

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

/ EDIT

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

  1. Определение размера стека с учетом размера безымянного типа параметра.
  2. Фактическое построение стека с переданным значением.

Оставим gcc 4.5 и msvc 2008 +

Ответы [ 4 ]

12 голосов
/ 20 мая 2011

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

[Добавлено]

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

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

7 голосов
/ 20 мая 2011

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

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

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

  • либо он встроен
  • , либо call фактически выдан

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

Когда выполняется вызов, он отправляется с определенным соглашением о вызовах.Каждый компилятор имеет свой собственный набор соглашений о вызовах, которые определяют, как передавать различные аргументы (указатель 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)
7 голосов
/ 20 мая 2011

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

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

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

2 голосов
/ 20 мая 2011

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

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