Значение против объекта

[С++, 17]

Когда выражение prvalue оценивается, стандарт говорит, что оно приводит к значению. В случае 5 выражение является предварительным значением и оценивается как значение 5.

Однако, когда у вас есть значение, в основном инициализатор для объекта, такой как Foo{}. Какова будет ценность этого выражения? Будет ли результат временным объектом, созданным преобразованием prvalue-xvalue? Это поднимает мой более широкий вопрос о разнице между ценностью и объектом.

Ответ 1

[intro.object]/1 :

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

Независимо от того, имеет ли prvalue тип класса, такой как Foo{} или нет, так как литерал 5 считается значением, и это значение затем используется для инициализации объекта, если это действительно необходимо, это когда значение материализуется в объект.

[class.temporary]/2:

Материализация временного объекта обычно задерживается как можно дольше, чтобы избежать создания ненужных временных объектов.

В том же разделе вы найдете список, описывающий, когда временные материализовались.

Ответ 2

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

Значение может иметь несколько представлений. Например, 10 $ могут быть представлены монетами или сохранены в виде битов на банковском счете.

Объектом является значение, которое является банковским счетом для некоторого количества денег: объект (/банковский счет) содержит представление значения (/10 $). Это описано в [basic.types]:

Представление значения объекта типа T - это набор битов, которые участвуют в представлении значения типа T. Биты в представлении объекта, которые не являются частью представления значения, являются битами заполнения.

Затем в [intro.object] указывается, что объект связан с областью хранения:

Объект занимает область хранения в период его строительства ([class.cdtor]), в течение всего срока его службы и в период разрушения ([class.cdtor]).

Это различие между объектом и его значением является более разумным, если мы рассмотрим абстрактную машину с центральным процессором, который выполняет операции, и отдельной памятью, где могут храниться объекты (которые содержат представление значения). Когда операция выполняется над значением, оно загружается в разные регистры процессора. Таким образом, значение в процессоре не имеет того же представления: непрерывная последовательность битов, как это было внутри объекта. Более того, любой процессор может свободно представлять значение в регистре как наиболее подходящий для его нужд.

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

Декомпозиция операции на операции со значениями и в хранилищах или нагрузках от или к объектам представлена в стандарте:

  • загрузка - это преобразование из lvalue-в-значение [conv.lvalue] glvalue нефункционального типа, не являющегося массивом T, может быть преобразовано в значение типа prvalue.

  • все операции в c++ приведут к последовательности фундаментальных операций со встроенным значением. Большинство из этих операций применяются к значению (prvalue), а не к объекту. Перед выполнением этих операций применяется значение lvalue-to-rvalue [expr] Всякий раз, когда выражение glvalue появляется в качестве операнда оператора, ожидающего значение prvalue для этого операнда, значение lvalue-to-rvalue, [...]

  • результаты этих встроенных операций, которые работают со значением, всегда являются prvalue (prvalue - это просто значение, не связанное ни с одним объектом). Затем полученное значение можно использовать как операнд другой встроенной операции или инициализировать объект (операция сохранения в памяти нашей машины), [basic.lval]: prvalue - это выражение, оценка которого инициализирует объект или битовое поле или вычисляет значение операнда оператора, как определено контекстом, в котором оно появляется. Таким образом, в нашем машинном представлении акт сохранения значения в объекте является хранилищем.

Чтобы проиллюстрировать это, давайте проанализируем этот простой фрагмент кода:

int main(int argc, char* argv[]){
  int j = 2*argc+1;
  }
  1. 2*argc Встроенный оператор 2*argc * вызывается с двумя аргументами 2 и argc. argc является lvalue, поэтому применяется значение lvalue-to-rvalue. Значение argc загружается в регистр процессора (2 может быть немедленным) и выполняется операция multiply.
  2. результат 2*argc - это значение, которое непосредственно используется в качестве первого операнда (2*argc)+(argc). Затем результирующее значение этой последней операции используется для инициализации объекта j: результирующее значение сохраняется в представлении памяти j.

Ответ 3

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

Рассмотрим эту простую программу:

std::string foo() { return std::string{"Hello"}; }

int main() {
    std::string f = foo();
}

foo не создает объект. Создание объекта потребовало бы вызова конструктора класса для начала жизни объекта. Для std::string это, вероятно, потребует выделения памяти и копирования символов, и по довольно очевидным причинам мы бы хотели избежать этого слишком много раз.

Вместо этого foo возвращает значение. Он возвращает концепцию "строки, инициализированной символами" Hello "". В конце концов, main может взять эту абстрактную концепцию и создать объект для представления этого значения. Из-за этого различия создается только один объект, поэтому дополнительные затраты на начало и конец времени жизни объекта должны быть оплачены только один раз.