C++. Специфікатор consexpr. Відмінності специфікатора constexpr від const

Специфікатор consexpr. Відмінності специфікатора constexpr від const. Приклади


Пошук на інших ресурсах:

 


Зміст


1. Специфікатор constexpr. Застосування для констант. Приклад

Специфікатор constexpr може застосовуватись:

  •  для змінних, які потрібно оголосити як константи (для констант);
  •  для функцій.

Загальна форма оголошення константи з специфікатором constexpr має вигляд

constexpr type ConstantName = value_or_expression;

тут

  • type – тип константи;
  • ConstantName – ім’я константи;
  • value_or_expression – значення (літерал) або вираз. У виразі може викликатись функція, яка позначена також як constexpr.

У випадку визначення для змінних-констант, цей специфікатор задає константи так само як і специфікатор const. Однак, значення цих констант повинні бути визначені одразу на етапі компіляції, а не сформовані пізніше на етапі виконання. Такими значеннями, які визначені одразу є літерали (2.88, “Hello, world!”, true).

Загальна форма оголошення функції з специфікатором constexpr наступна

constexpr return_type FuncName(list_of_parameters)
{
  // ...

  return const_expr_value;
}

тут

  • FuncName – ім’я функції, яка повертає константу;
  • list_of_parameters – список параметрів функції;
  • const_expr_value – значення, яке може обчислюватись на етапі компіляції. Це може бути результат виклику інших constexpr-функцій, значення літералів, значення інших констант, оголошених з специфікатором constexpr.

 

2. Відмінності між специфікаторами constexpr та const

Між специфікаторами const та constexpr є наступні відмінності:

1. Ініціалізація змінної const може бути відкладена до часу виконання:

// Тут sqrt() обчислюється пізніше, при виконанні програми
const double rc1 = sqrt(4.44 + c1);
cout << rc1 << endl;

2. Ініціалізація змінної constexpr не може бути відкладена до часу виконання:

double ce1 = 23.2;
constexpr double rce1 = sqrt(2.88); // не працює, оскільки sqrt() обчислиться пізніше

3. Ініціалізацію змінної constexpr можна обчислювати на основі іншої змінної,  яка також оголошена як constexpr

// 1. Ініціалізація константи constexpr на основі іншої константи constexpr
constexpr int ce1 = 50;
constexpr int ce2 = ce1 * 2 + 100; // ce2 = 200 - працює

також на основі константи, яка оголошена як const

// Спроба ініціалізації константи constexpr
// на основі іншої константи, оголошеної як const
const int c1 = 200;
constexpr int ce3 = c1 + 10; // працює, оскільки значення c1 відомо на етапі компіляції

4. Константу типу const можна оголошувати на основі змінної, а константу типу constexpr неможна

// Спроба ініціалізації константи const на основі змінної
int v1 = 100;
const int c2 = v1 * 3; // працює

// Спроба ініціалізації константи constexpr на основі змінної
int v2 = 100;
constexpr int ce4 = v2 * 3; // помилка, щоб виправити це потрібно v2 оголосити як constexpr

 

3. Специфікатор constexpr. Застосування для констант. Приклад
// Змінні constexpr допускається ініціалізовувати значенням літералу,
// оскільки значення літералу одразу визначене
constexpr double d = true;
constexpr double t{ 3.05 };

cout << d << endl;

// constexpr float x = exp(2.8); // помилка
const float x = exp(2.8); // працює

double f = 0.0;
//constexpr double v = f + 2.5; // помилка, f не є constexpr

// Ініціалізація змінної const може бути відкладена до часу виконання.

// 1. Для констант (const) це все працює
double c1 = 23.2;

// Тут sqrt() обчислюється пізніше, при виконанні програми
const double rc1 = sqrt(4.44 + c1);
cout << rc1 << endl;

// 2. Для констант constexpr це не працює,
// тобто ініціалізаці змінної constexpr не може
// бути відкладена до часу виконання.
// Ця ініціалізація повинна виконуватись одразу,
// інакше компілятор видасть помилку.
double ce1 = 23.2;
constexpr double rce1 = sqrt(2.88); // не працює, оскільки sqrt() обчислиться пізніше

constexpr double rce2 = sqrt(ce1); // не працює

constexpr double ce2 = 3.8;
constexpr double rce3 = sqrt(ce2); // не працює

 

4. Специфікатор constexpr. Застосування для функцій, які повертають константу. Приклад

Приклад.

#include <iostream>
using namespace std;

// Для змінних constexpr і const обов'язково повинен бути ініціалізатор.

// Функція, яка повертає число 10
int GetNumber10()
{
  return 10;
}

// Інша функція, яка повертає 20
constexpr int GetNumber20()
{
  return 20;
}

void TestVariable()
{
  // Ініціалізація змінної const може бути відкладена до часу виконання.

  // 1. Для констант (const) це все працює
  double c1 = 23.2;

  // Тут sqrt() обчислюється пізніше, при виконанні програми.
  const double rc1 = sqrt(4.44 + c1);
  cout << rc1 << endl;

  // 2. Для констант constexpr це не працює, тобто ініціалізаці змінної constexpr не може бути відкладена до часу виконання.
  // Ця ініціалізація повинна виконуватись одразу, інакше компілятор видасть помилку.
  double ce1 = 23.2;
  //constexpr double rce1 = sqrt(2.88); // не працює, оскільки sqrt() обчислиться пізніше

  //constexpr double rce2 = sqrt(ce1); // не працює
  constexpr double ce2 = 3.8;
  //constexpr double rce3 = sqrt(ce2); // не працює
}

void main()
{
  // Виклик функції

  // 1. Специфікатор const
  // Тут працює, оскільки значення буде отримане на етапі виконання.
  const int c1 = GetNumber10();

  // Тут також працює, але з попередженням помітити c2 як constexpr
  const int c2 = GetNumber20();
  cout << c2;

  // 2. Специфікатор cosnexpr
  // Не працює, оскільки constexpr-значення повинно отримуватись на етапі компіляції а не на етапі виконання
  // constexpr int ce1 = GetNumber10();

  // Працює, оскільки функція GetNumber20() помічена як така, що її виклик і
  // повернення значення повинні виконуватись на етапі компіляції
  constexpr int ce2 = GetNumber20();
  cout << ce2 << endl;
}

 

5. Оголошення констант constexpr на основі інших констант та літералів. Приклад

 

// 1. Ініціалізація константи constexpr на основі іншої константи constexpr
constexpr int ce1 = 50;
constexpr int ce2 = ce1 * 2 + 100; // ce2 = 200 - працює

// 2. Спроба ініціалізації константи constexpr на основі іншої константи, оголошеної як const
const int c1 = 200;
constexpr int ce3 = c1 + 10; // працює, оскільки значення c1 відомо на етапі компіляції

// 3. Спроба ініціалізації константи const на основі змінної
int v1 = 100;
const int c2 = v1 * 3; // працює

// 4. Спроба ініціалізації константи constexpr на основі змінної
int v2 = 100;
constexpr int ce4 = v2 * 3; // помилка, щоб виправити це потрібно v2 оголосити як constexpr

 

6. Приклад виклику з constexpr-функції іншої constexpr-функції. Ланцюжок викликів

 

#include <iostream>
using namespace std;

// Функція, яка повертає число 15
constexpr int GetNumber15()
{
  return 15;
}

// Інша constexpr-функція, яка повертає 20.
// Ця функція звертається до функції GetNumber15()
constexpr int GetNumber20()
{
  return 5 + GetNumber15();
}

void main()
{
  // 1. Ініціалізація константи constexpr на основі іншої константи constexpr
  constexpr int ce1 = 50;
  constexpr int ce2 = ce1 * 2 + 100; // ce2 = 200 - працює

  // 2. Спроба ініціалізації константи constexpr на основі іншої константи, оголошеної як const
  const int c1 = 200;
  constexpr int ce3 = c1 + 10; // працює, оскільки значення c1 відомо на етапі компіляції

  // 3. Спроба ініціалізації константи const на основі змінної
  int v1 = 100;
  const int c2 = v1 * 3; // працює

  // 4. Спроба ініціалізації константи constexpr на основі змінної
  int v2 = 100;
  //constexpr int ce4 = v2 * 3; // помилка, щоб виправити це потрібно v2 оголосити як constexpr

  // Виклик функції GetNumber20() для ініціалізації constexpr-константи з іменем d1
  constexpr double d1 = GetNumber20(); // 20 - працює
  cout << d1 << endl;
}

 


Споріднені теми