Для чего "constexpr" полезен?
Я действительно не могу его использовать. Моя первая идея заключалась в том, что я мог использовать его для реализации "Design by Contract" без использования таких макросов, как это:
struct S
{
S(constexpr int i) : S(i) { static_assert( i < 9, "i must be < 9" ); }
S(int i); //external defintion
char *pSomeMemory;
};
Но это не скомпилировалось. Я думал, что мы также можем использовать его для ссылки на одну и ту же переменную без необходимости создания дополнительной памяти, когда мы хотим избежать get/seters, чтобы сделать экземпляры для одного члена из пользователей доступными только для чтения:
class S
{
private:
int _i;
public:
const int & constexpr i = _i;
};
Но ни одно из вышеперечисленных фактически не скомпилировано. Может ли кто-нибудь дать мне некоторое представление о том, почему это ключевое слово вводилось?
Ответы
Ответ 1
Цель constexpr
зависит от контекста:
-
Для объектов это означает, что объект является неизменным и должен быть построен во время компиляции. Помимо перемещения операций во время компиляции, а не их выполнения во время выполнения, объекты constexpr
имеют дополнительное преимущество в том, что они инициализируются до создания любых потоков. В результате их доступ никогда не требует синхронизации. Пример объявления объекта как constexpr
будет выглядеть так:
constexpr T value{args};
Очевидно, для того, чтобы работать, args
должны быть постоянными выражениями.
-
Для функций это означает, что вызов функции может привести к постоянному выражению. Результат результата функции constexpr
вызывает постоянное выражение зависит от аргументов и определения функции. Непосредственная импликация заключается в том, что функция должна быть inline
(она будет неявно сделана так). Кроме того, существуют ограничения на то, что можно сделать в такой функции. Для С++ 11 функция может иметь только один оператор, который для неконструкторов должен быть return
-statement. Это ограничение было смягчено в С++ 14. Например, следующее определение функции constexpr
:
constexpr int square(int value) { return value * value; }
При создании объекта нестандартных типов constexpr
соответствующим типам понадобится конструктор constexpr
: созданный конструктор по умолчанию не будет работать. Очевидно, что конструктор constexpr
должен будет инициализировать все члены. Конструктор constexpr
может выглядеть так:
struct example {
int value;
constexpr example(int value): value(value) {}
};
int main() {
constexpr example size{17};
int array[size.value] = {};
}
Созданные значения constexpr
могут использоваться везде, где ожидается постоянное выражение.
Ответ 2
Как я его вижу, constexpr
- это способ объединить два языка С++ - тот, который выполняется во время выполнения, и тот, который выполняется во время компиляции. Программирование времени компиляции обычно называется метапрограммированием.
Сначала был C, с его макросами. Макросы были фактически небольшими программами, которые выполнял компилятор. У них были утверждения (называемые #ifdef
), переменные (с #define
). Там даже целый язык сценариев, который выполняется во время компиляции.
Когда С++ вышел, у него были макросы C и больше ничего. Затем появились шаблоны С++. Они ввели другой способ запуска кода времени компиляции. Мета-язык С++ был в основном функциональным, позволяя, например, делать циклы с хвостовой рекурсией.
В С++ 11 они решили, что метапрограммирование может выглядеть лучше, поэтому они представили constexpr
. Теперь вы можете написать функции С++, которые также являются мета-функциями. В С++ 14 он становится лучше, потому что ограничения на функции constexpr были ослаблены.
Ответ 3
Хотя это объясняется более подробно в "Constexpr - Обобщенные константные выражения в С++ 11", статья Алекса Аллана, я упрощу его с помощью нескольких простых точек.
РЕДАКТИРОВАТЬ: Извините, я был виноват в том, что мне кажется, что я был автором этих вопросов, и я исправил себя и изменил различные части и добавил цитаты, чтобы избежать плагизма.
Простое объяснение полезности:
- Сначала с помощью спецификатора Constexpr значение функции или переменной может быть во время компиляции.
- Еще одно преимущество Constexpr Specifier заключается в том, что он может заменить макросы функциями
- Constexpr также пригодится для метапрограммирования шаблонов.
Финал
константные выражения... позволяют выполнять определенные вычисления во время компиляции, буквально, в то время как ваш код компилируется, а не когда запускается сама программа. (Allain 2)
Преимущества производительности: если что-то можно сделать во время компиляции, это будет сделано один раз, а не каждый раз, когда программа запускает
Дополнительно:
Такие переменные и функции могут использоваться тогда, когда допускаются только компиляции констант времени. Спецификатор constexpr, используемый в объявлении объекта, подразумевает const. Спецификатор constexpr, используемый в объявлении функции, подразумевает inline. (CPP 1)
Некоторые простые правила для запоминания -
Правила:
- Он должен состоять из одного оператора return (за некоторыми исключениями)
- Он может вызывать только другие функции constexpr
- Он может ссылаться только на constexpr глобальных переменных (Allain 6)
Дополнительно:
Правила конструктора Constexpr:
- каждый из его параметров должен быть литеральным типом
- класс не должен иметь виртуальных базовых классов
- конструктор не должен иметь функцию-try-block (CPP 6)
Цитирование
Allain, Alex, "Constexpr - Обобщенные константные выражения в С++ 11", Unspecified Date, " http://www.cprogramming.com/c++11/c++11-compile-time-processing-with-constexpr.html"
CPP, "Constexpr Specifier", 16 декабря 2014 года, http://en.cppreference.com/w/cpp/language/constexpr
Ответ 4
Ответа на вопрос "Может ли кто-нибудь дать мне некоторое представление о том, почему вводилось ключевое слово constexpr?"
Современный С++ поддерживает два типа неизменяемости.
1) const
2) constexpr.
constexper будет оцениваться во время компиляции. Он используется для указания константы и позволяет размещать данные в памяти, где она может быть повреждена.
Пример 1:
void UseConstExpr(int temp)
{
// This code snippet is OK
constexpr int y = max + 300;
std::cout << y << std::endl;
// This code snippet gives compilation error
// [ expression must have a constant value]
constexpr int z = temp + 300;
}
Пример 2:
int addVector(const std::vector<int>& inVect)
{
int sum = 0;
for ( auto& vec : inVect)
{
sum += vec;
}
std::cout << sum << std::endl;
return sum;
}
int main()
{
// vInt is not constant
std::vector<int> vInt = { 1,2,3,4,5,6 };
// This code snippet is OK
// because evaluated at run time
const int iResult = addVector(vInt);
// Compiler throws below error
// function call must have a constant value in a constant expression
// because addVector(vInt) function is not a constant expression
constexpr int iResult = addVector(vInt);
return 0;
}
Примечание: выше исходный код скомпилирован на VS2015