Різниця між `constexpr` та` const`


593

Яка різниця між constexprі const?

  • Коли я можу використовувати лише одну з них?
  • Коли я можу використовувати обидва і як слід вибрати один?

71
constexprстворює константа часу компіляції; constпросто означає, що значення не можна змінити.
0x499602D2


Ця стаття з boost/hanaбібліотеки може висвітлити деякі constexprпроблеми, якими ви можете користуватися, constexprа де не можете: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Андрій

@ 0x499602D2 " просто означає, що значення неможливо змінити ". Для скаляра, ініціалізованого літералом , значення, яке неможливо змінити , також є постійною часом компіляції.
цікавогут

@curiousguy Так, мій коментар був дуже спрощеним. Справді, я теж був новим, constexprтому :)
0x499602D2

Відповіді:


587

Основне значення та синтаксис

Обидва ключові слова можуть бути використані в оголошенні об'єктів, а також функцій. Основна різниця при застосуванні до об'єктів полягає в наступному:

  • constоголошує об’єкт постійним . Це означає гарантію, що після ініціалізації значення цього об'єкта не зміниться, і компілятор може використовувати цей факт для оптимізації. Це також допомагає запобігти програмісту писати код, який модифікує об'єкти, які не мали бути змінені після ініціалізації.

  • constexprоголошує об'єкт придатним для використання в тому, що Стандарт називає постійними виразами . Але зауважте, що constexprце не єдиний спосіб зробити це.

При застосуванні до функцій основна різниця полягає в наступному:

  • constможе використовуватися лише для нестатичних функцій членів, а не функцій загалом. Це дає гарантію, що функція-член не змінює жодного з нестатичних членів даних.

  • constexprможе використовуватися як з членами, так і з не членами, а також з конструкторами. Він оголошує функцію придатною для використання в постійних виразах . Компілятор прийме його лише у тому випадку, якщо функція відповідає певним критеріям (7.1.5 / 3,4), головне (†) :

    • Орган функції повинен бути невіртуальним та надзвичайно простим: окрім typedefs та статичних тверджень, дозволено лише одне returnтвердження. У випадку конструктора дозволено лише список ініціалізації, typedefs та статичне затвердження. ( = defaultі = deleteвони теж дозволені.)
    • Що стосується C ++ 14, правила є більш розслабленими, що дозволено з тих пір усередині функції contexpr: asmдекларація, gotoзаява, заява з міткою, відмінною від caseта default, спробувати-блок, визначення змінної нелітерального типу, визначення змінної статичної тривалості або тривалості зберігання потоку, визначення змінної, для якої не виконується ініціалізація.
    • Аргументи та тип повернення повинні бути буквальними типами (тобто, загалом кажучи, дуже прості типи, як правило, скаляри або агрегати)

Постійні вирази

Як було сказано вище, constexprдекларується як об'єкти, так і функції, придатні для використання в постійних виразах. Постійний вираз є більш ніж просто постійним:

  • Він може бути використаний у місцях, які потребують оцінки часу компіляції, наприклад, параметри шаблону та специфікатори розміру масиву:

    template<int N>
    class fixed_size_list
    { /*...*/ };
    
    fixed_size_list<X> mylist;  // X must be an integer constant expression
    
    int numbers[X];  // X must be an integer constant expression
  • Але зверніть увагу:

    • Оголошення чогось constexprне обов'язково гарантує, що воно буде оцінено під час компіляції. Він може бути використаний для таких, але він може бути використаний і в інших місцях, які оцінюються під час виконання.

    • Об'єкт може бути придатним для використання в постійних виразах без декларування constexpr. Приклад:

      int main()
      {
        const int N = 3;
        int numbers[N] = {1, 2, 3};  // N is constant expression
      }

    Це можливо тому N, що , будучи постійним та ініціалізованим під час декларування з літералом, задовольняє критеріям постійного вираження, навіть якщо воно не оголошено constexpr.

То коли мені насправді потрібно користуватися constexpr?

  • Об'єкт , як Nвище , може бути використаний в якості постійного вираження , НЕ оголошуються constexpr. Це справедливо для всіх об'єктів, які є:

    • const
    • цілісного чи перелічувального типу та
    • ініціалізований під час оголошення виразом, який сам по собі є постійним виразом

    [Це пов’язано з §5.19 / 2: Постійний вираз не повинен включати субдекспресії, що передбачають "модифікацію значення-до-значення", якщо […] не є цінністю цілісного чи перелічувального типу […] "Дякую Річарду Сміту за виправлення мого раніше стверджували, що це було справедливо для всіх буквальних типів.]

  • Щоб функція була придатною для використання в постійних виразах, вона повинна бути чітко оголошена constexpr; для цього недостатньо лише задовольнити критерії функцій постійного вираження. Приклад:

    template<int N>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }

Коли я можу / повинен використовувати і те, constі constexpr разом?

A. У об’єктних деклараціях. Це ніколи не потрібно, коли обидва ключові слова посилаються на один і той же об'єкт, який потрібно оголосити. constexprмається на увазі const.

constexpr const int N = 5;

те саме, що

constexpr int N = 5;

Однак зауважте, що можуть виникати ситуації, коли ключові слова посилаються на різні частини декларації:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

Тут NPоголошується як адреса постійного виразу, тобто вказівник, який сам по собі є постійним виразом. (Це можливо, коли адреса генерується шляхом застосування оператора адреси до статичного / глобального постійного виразу.) Тут і те, constexprі constпотрібно: constexprзавжди посилається на вираз, який оголошується (тут NP), тоді як constпосилається на int(він оголошує покажчик- до-const). Видалення символу constзробить вираз незаконним (оскільки (a) вказівник на об'єкт, який не є const, не може бути постійним виразом, і (b) &Nнасправді є покажчиком на постійну).

B. У деклараціях про функції члена. У C ++ 11 constexprмається на увазі const, тоді як у C ++ 14 та C ++ 17 це не так. Функція члена, оголошена під C ++ 11 як

constexpr void f();

потрібно оголосити як

constexpr void f() const;

під C ++ 14, щоб все-таки використовуватись як constфункція.


3
ІМО "необов'язково оцінюється під час компіляції" є менш корисним, ніж думати про них як "оцінені під час компіляції". Обмеження постійного виразу означають, що компілятору було б відносно легко його оцінити. Укладач повинен скаржитися, якщо ці обмеження не виконуються. Оскільки побічних ефектів немає, ви ніколи не можете сказати різницю, "чи оцінив" компілятор це чи ні.
aschepler

10
@aschepler Звичайно. Моя головна думка в тому, що якщо ви викликаєте constexprфункцію з незмінним виразом, наприклад, звичайною змінною, це цілком законно, і функція буде використовуватися, як і будь-яка інша функція. Він не буде оцінюватися під час компіляції (бо не може). Можливо, ви вважаєте, що це очевидно - але якби я заявив, що функція, оголошена як constexprзавжди, буде оцінюватися під час компіляції, її можна було б трактувати неправильно.
jogojapan

5
Так, я говорив про constexprоб’єкти, а не про функції. Мені подобається думати constexprпро об'єкти як про примушення оцінювання значень часу компіляції, а constexprпро функції, що дозволяють оцінювати функцію під час компіляції або за необхідності виконувати час виконання.
aschepler

2
Виправлення: "const" - це лише обмеження, що ВАС не може змінити значення змінної; це не дає ніяких обіцянок, що значення зміниться (тобто, кимось іншим). Це властивість запису, а не властивість читання.
Джаред Грубб

3
Це речення: Це дає гарантію, що функція-член не змінює жодного з нестатичних членів даних. пропускає одну важливу деталь. Члени, позначені як такі, mutableтакож можуть бути змінені constфункціями членів.
всезначний

119

constзастосовується до змінних і не дозволяє їх змінювати у вашому коді.

constexprповідомляє компілятору, що це вираження призводить до постійного значення часу компіляції , тому його можна використовувати в таких місцях, як довжина масиву, присвоюючи constзмінні тощо. Посилання, яке надає Oli, має безліч чудових прикладів.

В основному, це 2 різні поняття взагалі, і їх можна (і слід) використовувати разом.


2
const & constexpr використання, напр .: en.cppreference.com/w/cpp/container/array/get
Manohar Reddy Poreddy

64

Огляд

  • constгарантує, що програма не змінює значення об'єкта . Однак constне гарантує, якому типу ініціалізації піддається об'єкт.

    Поміркуйте:

    const int mx = numeric_limits<int>::max();  // OK: runtime initialization

    Функція max()просто повертає буквальне значення. Однак, оскільки ініціалізатор є функцією виклику, він mxзазнає ініціалізації виконання. Тому ви не можете використовувати його як постійний вираз :

    int arr[mx];  // error: “constant expression required”
  • constexprце нове ключове слово C ++ 11, яке позбавляє вас від необхідності створювати макроси та жорсткі коди. Це також гарантує, що за певних умов об'єкти проходять статичну ініціалізацію . Він контролює час оцінки виразу. Застосовуючи оцінку часу вираження часу компіляції , constexprви зможете визначити справжні постійні вирази, які мають вирішальне значення для критично важливих для часу програм, системного програмування, шаблонів і взагалі кажучи, у будь-якому коді, який спирається на константи часу компіляції.

Функції постійного вираження

Функція постійного вираження - це оголошена функція constexpr. Його корпус повинен бути невіртуальним і складатися лише з однієї заяви повернення, окрім typedefs та статичних тверджень. Його аргументи та повернене значення повинні мати буквальний тип. Він може використовуватися з аргументами нестаціонарного вираження, але коли це зроблено, результат не є постійним виразом.

Функція постійної експресії призначена для заміни макросів і твердо кодованих літералів без шкоди для продуктивності чи безпеки.

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

Об'єкти з постійною експресією

Об'єкт постійного вираження є об'єктом оголошений constexpr. Він повинен бути ініціалізований постійним виразом або оцінкою, побудованою конструктором з постійним виразом з аргументами постійного вираження.

Об'єкт постійної експресії поводиться так, ніби він був оголошений const, за винятком того, що він вимагає ініціалізації перед використанням, і його ініціалізатор повинен бути постійним виразом. Отже, об'єкт постійної експресії завжди може бути використаний як частина іншого виразу постійного виразу.

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

Конструктори з постійною експресією

Постійне вираз-конструктор є конструктором оголошена constexpr. Він може мати список ініціалізації членів, але його тіло повинно бути порожнім, окрім typedefs та статичних тверджень. Його аргументи повинні мати буквальний тип.

Конструктор з постійним виразом дозволяє компілятору ініціалізувати об'єкт під час компіляції за умови, що аргументи конструктора - це всі постійні вирази.

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

Поради з книги « Ефективний сучасний C ++ » Скотта Майєрса про constexpr:

  • constexpr об'єкти const і ініціалізуються значеннями, відомими під час компіляції;
  • constexpr функції створюють результати часу компіляції, коли викликаються аргументами, значення яких відомі під час компіляції;
  • constexprоб'єкти та функції можуть використовуватися в більш широкому діапазоні контекстів, ніж необ'єкти constexprта функції;
  • constexpr є частиною інтерфейсу об'єкта або функції.

Джерело: Використання constexpr для поліпшення безпеки, продуктивності та інкапсуляції в C ++ .


Дякуємо за чудовий приклад коду, що показує різні ситуації. Як чудово, як і деякі інші пояснення, я вважаю, що бачення коду в дії набагато корисніше і зрозуміліше. Це дійсно допомогло зміцнити моє розуміння того, що відбувається.
RTHarston

35

Згідно з книгою "Мова програмування C ++ 4-го редактора" Bjarne Stroustrup
const : приблизно означає "" Я обіцяю не змінювати це значення "(§7.5). Це використовується в основному для визначення інтерфейсів, щоб дані могли передаватись функціям, не боячись їх змінити.
Компілятор виконує обіцянку, зроблену const.
constexpr : означає приблизно "" підлягає оцінці під час компіляції "" (§10.4). Це використовується в основному для визначення констант, щоб дозволити,
наприклад:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

Щоб функція була корисною в постійному виразі, тобто в виразі, який буде оцінено компілятором, вона повинна бути визначена constexpr .
Наприклад:

constexpr double square(double x) { return xx; }


Щоб бути constexpr, функція повинна бути досить простою: просто оператор return, який обчислює значення. Функція constexpr може використовуватися для непостійних аргументів, але коли це зроблено, результат не є постійним виразом. Ми дозволяємо викликати функцію constexpr аргументами нестаціонарних виразів у контекстах, які не потребують постійних виразів, так що ми не повинні два рази визначати одну і ту ж функцію: один раз для постійних виразів і один раз для змінних.
У кількох місцях потрібні постійні вирази мовними правилами (наприклад, межі масивів (§2.2.5, §7.3), мітки регістрів (§2.2.4, §9.4.2), деякі аргументи шаблону (§25.2) та константи, оголошені за допомогою constexpr). В інших випадках оцінка часу складання важлива для продуктивності. Незалежно від питань ефективності, поняття незмінності (об'єкта із незмінним станом) є важливим дизайнерським питанням (§10.4).


Є проблеми з продуктивністю. Здається, що функція constexpr, якщо вона оцінюється під час виконання, може бути повільнішою, ніж версія, яка не є constexpr. Якщо у нас є постійне значення, чи слід віддавати перевагу "const" або "constexpr"? (більше стильового питання, створеного збіркою, виглядає так само)
CoffeDeveloper

31

І те, constі constexprможе бути застосоване до змінних та функцій. Хоча вони схожі між собою, насправді це дуже різні поняття.

Обидва constі constexprозначають, що їх значення не можуть бути змінені після їх ініціалізації. Так, наприклад:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

Принципова відмінність між часом constта constexprчасом, коли їхні значення ініціалізації відомі (оцінені). Хоча значення constзмінних можна оцінювати як під час компіляції, так і під час виконання, constexprвони завжди оцінюються під час компіляції. Наприклад:

int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

Ключовою перевагою, щоб знати, чи знається значення під час компіляції чи час виконання, - це те, що константи часу компіляції можна використовувати, коли потрібні константи часу компіляції. Наприклад, C ++ не дозволяє вказувати C-масиви зі змінною довжиною.

int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

Отже, це означає, що:

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

Таким чином, constзмінні можуть визначати обидві константи часу компіляції, такі, size1які можуть бути використані для визначення розмірів масиву та констант виконання, таких size2як відомі лише під час виконання, і не можуть використовуватися для визначення розмірів масиву. З іншого боку, constexprзавжди визначайте константи часу компіляції, які можуть визначати розміри масиву.

І те, constі constexprінше можна застосувати і до функцій. constФункція повинна бути функцією членом (метод, оператор) , де застосуванням constключового слова означає , що метод не може змінити значення їх члена (не статичною) полів. Наприклад.

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

А constexpr- це інше поняття. Він позначає функцію (член або не член) як функцію, яку можна оцінити під час компіляції, якщо константи часу компіляції передаються як їх аргументи . Наприклад, ви можете написати це.

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

До речі, constexprфункції - це звичайні функції C ++, які можна викликати, навіть якщо передаються непостійні аргументи. Але в цьому випадку ви отримуєте значення non-constexpr.

int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

Також constexprможна застосувати до функцій (методів) членів, операторів і навіть конструкторів. Наприклад.

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

Більш "божевільний" зразок.

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.

Крім того, в constexpr intconst int
мові

8

Як вже зазначалося @ 0x499602d2, constлише гарантується, що значення не може бути змінено після ініціалізації, коли as constexpr(введене в C ++ 11) гарантує, що змінна є постійною часом компіляції.
Розглянемо наступний приклад (від LearnCpp.com):

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime

5

A const int varможна динамічно встановити значення під час виконання, і як тільки він буде встановлений на це значення, його більше не можна змінювати.

A constexpr int varне може бути динамічно встановлено під час виконання, а, скоріше, під час компіляції. І як тільки воно буде встановлено на це значення, воно більше не може бути змінено.

Ось вагомий приклад:

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

Вище фрагмент добре поєднується, і я прокоментував ті, що спричиняють його помилку.

Основними поняттями, які тут слід взяти до уваги, є поняття про compile timeта run time. У C ++ були введені нові інновації, призначені для максимально можливих ** know **певних речей під час компіляції для покращення продуктивності під час виконання.


3

Я не думаю, що жоден з відповідей насправді дає чітко зрозуміти, які побічні ефекти у нього є, чи справді, що це таке.

constexprі constв просторі імен / файлі-області є ідентичними, коли ініціалізуються з літералом або виразом; але функція constможе бути ініціалізована будь-якою функцією, але constexprініціалізована non-constexpr (функція, яка не позначена constexpr або не constexpr виразом) призведе до помилки компілятора. І те, constexprі constнеявно є внутрішнім зв’язком для змінних (насправді, вони не виживають, щоб дістатися до стадії зв’язку, якщо збирають -O1 і більш сильні, і staticне змушують компілятора випромінювати внутрішній (локальний) символ посилання для constабо constexprколи в -O1 або сильніше; єдиний раз, коли це буде зроблено, це якщо ви візьмете адресу змінної. constІ constexprбуде внутрішнім символом, якщо не виражено символом externieextern constexpr/const int i = 3;потрібно використовувати). У функції constexprробить функція постійно ніколи не досягає стадії зв'язування (незалежно від externабо inlineу визначенні або -O0 або -Ofast), тоді як constніколи не робить staticі inlineтільки має такий вплив на -O1 і вище. Коли const/ constexprзмінна ініціалізується constexprфункцією, навантаження завжди оптимізується за допомогою будь-якого прапора оптимізації, але вона ніколи не оптимізується, якщо функція є лише staticабо inline, або якщо змінна не є const/ constexpr.

Стандартна компіляція (-00)

#include<iostream>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

компілює до

val:
        .long   100  //extra external definition supplied due to extern

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 100 //substituted in as an immediate
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 

Однак

#include<iostream>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int main () {
  std::cout << val;
}

Компілює до

multiply(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR val[rip]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 
        mov     esi, 10
        mov     edi, 10
        call    multiply(int, int)
        mov     DWORD PTR val[rip], eax

Це чітко показує, що constexprприводить ініціалізація const/constexprзмінної області файлу до часу компіляції та не створює глобального символу, тоді як не використовуючи її, ініціалізація виникає раніше mainпід час виконання.

Складання за допомогою -Ofast

Навіть -Ofast не оптимізує навантаження! https://godbolt.org/z/r-mhif , тому потрібно constexpr


constexprфункції можна також викликати всередині інших constexprфункцій для того ж результату. constexprфункція також запобігає використанню всього, що неможливо зробити під час компіляції у функції; наприклад, дзвінок <<оператору на std::cout.

constexprв області блоку поводиться так само, що створює помилку, якщо ініціалізується функцією, що не відповідає конспекту; значення також заміщується негайно.

Зрештою, його головне призначення схоже на вбудовану функцію C, але вона ефективна лише тоді, коли функція використовується для ініціалізації змінних файлових областей (які функції не можуть виконувати на C, але вони можуть на C ++, тому що це дозволяє динамічну ініціалізацію файлів, змінні області), за винятком того, що функція не може експортувати глобальний / локальний символ також до посилання, навіть не використовуючи extern/static, що ви могли б використовувати inlineна C; Функції присвоєння змінної блокової області можуть бути впорядковані просто за допомогою оптимізації -O1 без constexprC і C ++.


Приємна точка на лінкері. Чи можна вважати більш безпечним взагалі використання constexpr, оскільки це призводить до меншої кількості витоків символів?
Ніл Макгілл

1
@NeilMcGill насправді не тому, що вбудований і статичний призведе до того, що компілятор не випромінює локальний символ для множення при компіляції за допомогою -O1 або більш сильної версії. Constexpr - це єдиний, який оптимізує навантаження для val, але крім того, що він ідентичний ставленню статичного або вбудованого перед функцією. Я ще щось забув. Constexpr - єдине ключове слово, яке не виділяє символ для функції на -O0, static та inline do
Lewis Kelsey

1

Перш за все, обидва є кваліфікаторами в c ++. Змінна оголошена const повинна бути ініціалізована і не може бути змінена в майбутньому. Отже, як правило, змінна, оголошена як const, матиме значення ще до компіляції.

Але для constexpr це трохи інакше.

Для constexpr ви можете надати вираз, який можна було б оцінити під час компіляції програми.

Очевидно, що змінна, оголошена констекстером, не може бути змінена в майбутньому так само, як const.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.