Основы. Приоритет и порядок (скобочное огораживание)

Задания раздела 4.1.2
4.1. Какое значение возвратит выражение 5+10*20/2?
4.2. Используя таблицу раздела 4.12, расставьте скобки в следующих выражениях, чтобы обозначить порядок группировки операндов:
(a) *vec.begin()
(b) *vec.begin()+1

Задания раздела 4.1.3
4.3.
Порядок вычисления большинства парных операторов оставляется неопределенным, чтобы предоставить компилятору возможность для оптимизации. Эта стратегия является компромиссом между созданием эффективного кода и потенциальными проблемами в использовании языка программистом. Полагаете этот компромисс приемлемым? Кто-то да, кто-то нет.

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

Немного конспекта:
унарные операторы воздействуют на один операнд (& - обращение к адресу, * - обращение к значению)
бинарные операторы воздействуют на два операнда (==равенство)
Значение большинства операторов типов классов можно определить самостоятельно. Поскольку такие определения придают альтернативное значение существующему символу оператора, они называются перегруженными операторами. Операторы >>, << библиотеки ввода и вывода, а так же операторы, использовавшиеся с объектами строк, векторов и итераторов, являются перегруженными.

 L- и r-значения (относительно оператора).
При применении объекта в качестве r-значения используется его значение (содержимое). При применении объекта в качестве l-значения используется его идентификатор (область в памяти).
Операторы различают по тому, требуют ли они операндов l- или r- значения, а так же по тому возвращают ли они l- или r- значения.
l-значение можно использовать там, где используется r-значение, а использовать r-значение там где требуется l-значение (область) - нельзя. Когда l-значение применяется вместо r-значения, используется содержимое объекта (его значение). Примеры:
1. В качестве своего левого операнда оператор присвоения требует l-значения и возвращает свой левый операнд как l-значение.
2. Оператор обращения к адресу требует в качестве операнда l-значение и возвращает указатель на свой операнд как r-значение.
3. Встроенные операторы обращения к значению и индексирования, а так же обращение к значению итератора и операторы индексирования строк и векторов возвращают l-значения.
4. Операторы инкремента и декремента, как встроенные, так и итераторы, требуют l-значения в качестве операндов. Их префиксные версии так же возвращают l-значения.
Рассматривая операторы, следует обратить внимание на то, должен ли операнд быть l-значением и возвращает ли он l-значение.
Все это для меня с первого или второго прочтения трудно понять на 100%, но на интуиции я начинаю понимать чем здесь говорится. Надо вместо l-значения проговаривать "область в памяти" а r-значения - "содержимое". Без этого понимания никакого программирования не получится.

При применении спецификатора decltype к выражению (отличному от переменной) результатом будет ссылочный тип, если если выражение возвращает l-значение. Предположим, например, что указатель p имеет тип int*. Поскольку обращение к значению возвращает l-значение, выражение decltype(*p) имеет тип int&. С другой стороны, поскольку оператор обращения к адресу возвращает r-значение, выражение decltype(&p) имеет тип **int, т.е. указатель на указатель на тип int.

4.1. Какое значение возвратит выражение 5+10*20/2?
105

4.2. Используя таблицу раздела 4.12, расставьте скобки в следующих выражениях, чтобы обозначить порядок группировки операндов:
(a) *vec.begin()
(b) *vec.begin()+1

Таблица большая, страшная и вообще для этого упражнения совсем не нужная. Лучше написать программу и пощупать. Из школьного курса математики известно, что скобками надо огораживать то, к чему ты будешь применять какое-то действие. Сначала выполняем действие в скобках, а потом все остальное. Можно навернуть еще больше скобок, но усложнять пока не буду. Что имеется в виду в вопросе (a) мне ясно не совсем, но предположу, что сначала возвращаю позицию первого элемента вектора, а потом желаю обратиться к его значению.

(a) *(vec.begin())
(b) *(vec.begin()+1)

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

#include <iostream>
#include <vector>
int main ()
{
std::vector<int> v1;
for (int i=0; i<10; ++i) v1.push_back(42+i);
v1[1]=50;
for (auto i : v1) std::cout<< i << " ";
std::cout << std::endl;
int p=*(v1.begin());
std::cout << p << " " << &*(v1.begin()) << std::endl;
int p1=*v1.begin();
std::cout << p1 << " "<< &*v1.begin() << std::endl;
int p2=*v1.begin()+1; // прибавляю к значению первого элемента единицу
std::cout << p2 << " "<< &*v1.begin()+1<< std::endl;
int p3=*(v1.begin()+1); /* перемещаю запращиваемое значение с первого элемента на второй и обращаюсь к его значению */
std::cout << p3 << " "<< &*(v1.begin()+1)<<std::endl;;
std::cout << &v1[1];
return(0);
}

Выводы такие: огораживание скобками работает. Компилятор сам разбирается с записью &*v1.begin()+1, и без всяких скобок он понимает, что нужен адрес второго значения вектора.

4.3. Порядок вычисления большинства парных операторов оставляется неопределенным, чтобы предоставить компилятору возможность для оптимизации. Эта стратегия является компромиссом между созданием эффективного кода и потенциальными проблемами в использовании языка программистом. Полагаете этот компромисс приемлемым? Кто-то да, кто-то нет.

Простое упражнение, надо ответить да или нет.
Слишком просто. Я уже убедился множество раз, что компилятор умнее меня. Пусть сам решает что ему лучше и когда. А когда стану умнее компилятора, тогда и прижму ему хвост! Боюсь только, что он сам не стоит на месте и пока я сплю он качается.

Добавить комментарий