Передача аргументов функции constexpr
Я новичок в программировании, и я начал узнавать об этом, используя книгу Принципы программирования и практику с использованием С++. Сегодня я здесь, потому что у меня есть некоторые проблемы в понимании функций constexpr
. В главе 8 автор вводит их в некоторые строки и краткий пример, используя эти слова:
Функция представляет собой вычисление, и иногда мы хотим сделать расчет во время компиляции. Причина, по которой требуется вычисление, вычисляемое компилятором, обычно заключается в том, чтобы избежать того же вычисления, выполненного миллионы раз во время выполнения.
Мы сообщаем, что будем иметь функцию, оцененную во время компиляции, объявив функцию как функцию constexpr
. Функция constepxr
может быть оценена во время компиляции, только если ей заданы постоянные выражения в качестве аргументов.
constexpr double xscale = 10; // scaling factors
constexpr double yscale = 0.8;
constexpr Point scale(Point p) { return { xscale*p.x, yscale*p.y }; };
Предположим, что точка - это простая структура с элементами x и y, представляющими двумерные координаты. Теперь, когда мы даем аргумент scale()
a Point
, он возвращает точку с координатами, масштабированными в соответствии с факторами xscale
и yscale
. Например:
void user(Point p1) {
Point p2{10,10};
Point p3 = scale(p1);
Point p4 = scale(p2) // p4 == {100,8}
constexpr Point p5 = scale(p1); // error : scale(p1) is not a constant expression
constexpr Point p6 = scale(p2); // p6 == {100,8};
Мой вопрос: Почему мы можем использовать p2
в качестве аргумента для scale()
? Является ли p2
постоянным выражением? И если да, то почему?
Можно ли считать элементы данных x
и y
постоянными выражениями?
Моя книга не дает слишком много информации, поэтому у меня возникают некоторые проблемы с этой концепцией.
Ответы
Ответ 1
В принципе, функции constexpr
могут выполняться во время компиляции или времени выполнения в зависимости от контекста. Гарантируется выполнение во время компиляции только в том случае, если все его параметры constexpr
, и его результат используется в контексте, требующем constexpr
(например, присвоение значения constexpr
, параметр шаблона или, скажем, размер массива c-style). В противном случае он оценивается во время выполнения как любая другая функция. Таким образом, строки p3
и p4
выполняются во время выполнения, тогда как p5
дает ошибку, потому что scale(p1)
не constexpr
, и фактически p6
также должен давать вам ошибку, если вы не добавите constexpr
к определению p2
. Пример здесь.
Ответ 2
Почему мы можем использовать p2
в качестве аргумента для scale()
?
Потому что scale()
записывается, чтобы принять что-либо, что является Point
или неявно конвертируется в Point
. В этом случае p2
имеет тип Point
. Следовательно, его можно использовать в качестве аргумента для scale()
.
Рассматривается ли p2 как постоянное выражение? И если да, то почему?
p2
фактически объявляется как локальная переменная. Когда он используется как:
constexpr Point p6 = scale(p2);
его значение вычисляется во время выполнения, используя вызов функции, и, следовательно, является ошибкой. Чтобы сделать эту работу, удалите ключевое слово constexpr
. Если вы хотите, чтобы он работал с constexpr
, сначала объявите p2
как constexpr
.
В следующем случае:
constexpr Point p5 = scale(p1);
p1
передается как аргумент user()
и может иметь любое значение, которое может быть известно только во время выполнения, следовательно, ошибка.
Ответ 3
Мне кажется, что он получил в книге. Похоже, что это должно быть
constexpr Point p2{10,10};
только в этом случае любой современный компилятор не даст ошибка по вызову
constexpr Point p6 = scale(p2); // p6 == {100,8};
потому что вы пытаетесь инициализировать переменную constexpr
с результатом функции, которая будет оцениваться во время выполнения (если p2
не объявлено constexpr
).