Что такое переменная в C++?

Понятно, что x тут переменная:

int main() {
  int x;
}

Ну а если это член структуры или шаблонный параметр? Это уже не переменные, хоть они и могут меняться?

struct A {
  int x;
  static int y;
  template<int z> void foo();
};

Ответы (3 шт):

Автор решения: rotabor

Изначально, переменная - это условное обозначение места в памяти для хранения данных. Потом, с усложнением языков программирования, появилась новая терминология - свойства, поля, и пр., которая отражает способ использования данных, назначение данных и другие характеристики. А термин переменная стал, как мне кажется, общим названием всех этих вариантов, так как суть его не изменилась. Конечно, в каких-то спецификациях, как пример, приведённый в комментарии, это темин может быть определён конкретно.

→ Ссылка
Автор решения: Тарас Атавин

Это тоже переменные. Есть общее понятие – величина. Величины бывают, во-первых, скалярные и не скалярные. Во-вторых, они делятся на константы и переменные. Переменная – это величина, которая может меняться, константа меняться не может. А скалярная величина – это величина, которая имеет ровно одно значение в каждый момент. Не скалярная – это любая другая. Структуры, например, состоят из других величин, каждая из которых имеет собственное имя. Каждая из них имеет и собственное значение. Поэтому структуры не скалярны. Но они тоже могут быть переменными. Или константами. При этом эти величины (называемые полями) тоже могут быть не скалярными. А экземпляры классов, называемые ещё объектами, ещё и объединяют данные с кодом, который с ними работает. Изначально различие в этом, а на c++ – почему-то в видимости членов по умолчанию. Термин «член» – это собирательное название для величин-членов, называемых полями, подпрограмм-членов, называемых методами, и операторов-членов. На c++ поддерживается лишь один вид методов – функции-члены. А ещё есть массивы. Массивом называется величина, состоящая из элементов, каждый из которых адресуется по некоторому количеству индексов в зависимости от размерности массива. На c/c++ массивы всегда линейны, это значит, что каждый элемент адресуется ровно одним индексом. На паскале массивы могут быть и, например, двумерными, тогда индексов два. Причём, каждый элемент адресуется обоими индексами сразу. Конкретный элемент адресуется в зависимости от сочетания значений всех индексов, при этом важен их порядок. То есть, например, a[2,3] и a[3,2] адресуют разные элементы. Каждый элемент тоже имеет собственное значение, поэтому массив скалярным не бывает. Все элементы одного и того же массива имеют один номинальный тип. Даже если каждый элемент может иметь собственный класс, фактические классы всех элементов будут разновидностями одного класса. Классы – это тоже типы. Массивы бывают: константные, статические, динамические, разреженные, логические и физические. Константный массив – это единственный вид массивов, которые являются константами, остальные массивы – переменные. Константный массив – это массив, у которого постоянно и количество элементов, и сами элементы. Статический массив – это массив, у которого постоянно количество элементов, при этом он может быть и автоматической переменной. А вот сами элементы – переменные, весь массив тоже переменная. Динамический массив – это массив, у которого переменно даже количество элементов. Разреженным массивом называется массив, лишь часть элементов которого фактически хранится. Если есть разреженный массив, то у него есть логический массив и физический массив. Логический массив – это массив, состоящий из всех номинально существующих элементов разреженного массива. А физический массив может вообще не быть массивом, но в любом случае состоит только из фактически хранимых элементов разреженного массива. И физический массив, и логический массив одного и того же разреженного массива могут независимо друг от друга быть динамическими, а могут – статическими. Есть ещё списки. Списком называется величина, состоящая из номинально однотипных элементов (как элементы массива), каждый из которых доступен через своих соседей. И как раз список – одна из рекомендованных реализаций физического массива. Наравне с массивом списков и списком массивов. Список – это тоже переменная. Наверное, всегда. Но каждый элемент списка имеет собственное значение, поэтому список скалярным тоже не бывает. Инты, чары (символы), даблы, флоаты, флаги (логические величины) – это скалярные величины. А структуры, объекты, массивы, списки, строки – нет. Но переменными бывают и те, и другие. Переменная может состоять из других переменных, а константа – из других констант, это нормально. Есть лишь две сущности, которые, не будучи настоящими переменными, могут меняться: формальный параметр и формальный операнд. Константами ни формальный параметр, ни формальный операнд тоже не являются, даже если им запрещено меняться. Величина, имеющая машинное представление, называется данное.

→ Ссылка
Автор решения: HolyBlackCat

Объект - это такая штука, которая создается во время выполнения1 программы, занимает память2, имеет тип и определенное значение. Функции и ссылки - не объекты.

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

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

Не все объекты создаются объявлениями. Например, 42 - объект без объявления3 (не переменная), а x в int x; - объект с объявлением и переменная.

Ссылки не являются объектами. Все ссылки создаются объявлениями.4

Переменные бывают безымянными, например безымянные параметры функций.

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

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

Переменные существуют только во время компиляции. Во время выполнения от них остаются только объекты/ссылки, на которые те ссылались.

Ну и для полноты:

Выражение - это часть текста программы, содержащая операнды, соединенные операторами. например 1 + 1, а также отдельные операнды, например просто 1.

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

Категории (lvalue, rvalue, ...) - есть у выражений, не у объектов. И наоборот, "временными" бывают объекты, но не выражения.


1 В constexpr-вычислениях они могут создаваться и во время компиляции.

2 Компилятор может соптимизировать объект, чтобы он не хранился в памяти (а, например, в регистрах, или вообще нигде). Но компилятор обязан делать это незаметно для программиста, поэтому об этом можно не думать (есть смысл думать только о том, как это влияет на производительность).

3 Немного приврал. Prvalue-выражения - не обозначают объекты, поскольку не занимают память и не существуют во время выполнения. Поэтому 42 сам по себе - не объект. Если его материализовать в xvalue, например создав ссылку на него - будет объект.

4 Например, int &x = y; - ссылка, созданная объявлением. А int &foo(); не объявляет ссылку (только функцию с возвращаемым типом-ссылкой), но и никакой ссылки при вызове не образуется. Результат вызова foo() - это lvalue типа int (не int &, ведь выражения не бывают ссылками).

5 Не путать тип выражения и тип переменной. Тип переменной - это просто тип, указанный при ее создании; а тип выражения, состоящего из одного только имени этой переменной - это тот же тип, но без ссылочности, если она была. Пример: для int &&x, тип переменной - int &&, тип выражения x - int, и категория выражения - lvalue.

decltype() искусственно добавляет ссылочность к типам выражений, в зависимости от их категории (lvalue = &, xvalue = &&, prvalue = ничего).

decltype обычно работает с выражениями, но для переменных у него специальное поведение - для них он просто возвращает их тип (без учета категории выражения и без убранной ссылочности). Поэтому для int &&x, decltype(x) - int && (тип переменной), а decltype((x)) - int & (он не считает (x) переменной и использует правила для выражений; тип выражения int, плюс & обозначающий lvalue).

→ Ссылка