Чи є різниця між наступними визначеннями?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Якщо ні, то який стиль є кращим у С ++ 11?
Чи є різниця між наступними визначеннями?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Якщо ні, то який стиль є кращим у С ++ 11?
Відповіді:
Я вважаю, що є різниця. Давайте перейменовамо їх, щоб ми могли про них говорити простіше:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Обидва PI1
і PI2
постійні, тобто ви не можете їх змінювати. Однак лише PI2
константа часу компіляції. Вона повинна бути ініціалізована під час компіляції. PI1
може бути ініціалізовано під час компіляції або час виконання. Крім того, тільки PI2
може бути використаний в контексті , який вимагає постійного часу компіляції. Наприклад:
constexpr double PI3 = PI1; // error
але:
constexpr double PI3 = PI2; // ok
і:
static_assert(PI1 == 3.141592653589793, ""); // error
але:
static_assert(PI2 == 3.141592653589793, ""); // ok
Що ви повинні використовувати? Використовуйте те, що відповідає вашим потребам. Ви хочете переконатися, що у вас є константа часу компіляції, яку можна використовувати в контекстах, де потрібна константа часу компіляції? Ви хочете мати можливість ініціалізувати його з обчисленнями, виконаними під час виконання? І т.д.
const int N = 10; char a[N];
роботи та межі масиву повинні бути константами часу компіляції.
PI1
на інтегральну константу часу компіляції для використання в масиві, але не для використання як нетиповий параметр інтегрального шаблону. Тож конвертованість часу на компіляцію PI1
в цілісний тип здається мені невеликим ударом і недоліком.
enum
ініціалізатора - це єдині дві помітні відмінності між const
і constexpr
(і жоден з них не працює double
).
1 / PI1
і 1 / PI2
може призводити до різних результатів. Я не думаю, що ця технічність є не такою важливою, як рекомендації у цій відповіді.
constexpr double PI3 = PI1;
для мене це працює правильно. (MSVS2013 CTP). Що я роблю неправильно?
Тут немає різниці, але це важливо, коли у вас є тип, який має конструктор.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
є константою, але вона не обіцяє бути ініціалізованою під час компіляції. s1
позначено constexpr
, так що вона є константою і, оскільки S
конструктор також позначений constexpr
, він буде ініціалізований під час компіляції.
Переважно це має значення, коли ініціалізація під час виконання буде трудомісткою, і ви хочете відсунути цю роботу на компілятор, де це також забирає багато часу, але не сповільнює час виконання компільованої програми
constexpr
призвів до встановлення діагнозу, якщо обчислення об'єкта за часом складання неможливо. Що менш зрозуміло, чи може функція, яка очікує постійного параметра, може виконуватися під час компіляції, якщо параметр повинен бути оголошений як, const
а не як constexpr
: тобто, буде constexpr int foo(S)
виконуватися під час компіляції, якщо я дзвоню foo(s0)
?
foo(s0)
було б виконано під час компіляції, але ти ніколи не знаєш: компілятору дозволено робити такі оптимізації. Звичайно, ні gcc 4.7.2, ні clang 3.2 не дозволяють мені збиратиconstexpr a = foo(s0);
constexpr вказує значення, яке є постійним і відомим під час компіляції.
const вказує значення, яке є лише постійним; це не обов'язково знати під час компіляції.
int sz;
constexpr auto arraySize1 = sz; // error! sz's value unknown at compilation
std::array<int, sz> data1; // error! same problem
constexpr auto arraySize2 = 10; // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr
Зауважте, що const не пропонує ту саму гарантію, що і constexpr, оскільки об'єкти const не потрібно ініціалізувати зі значеннями, відомими під час компіляції.
int sz;
const auto arraySize = sz; // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation
Усі об’єкти constexpr є const, але не всі об'єкти const є constexpr.
Якщо ви хочете, щоб компілятори гарантували, що змінна має значення, яке можна використовувати в контекстах, що вимагають констант часу компіляції, інструментом, до якого слід звернутися, є constexpr, а не const.
Constexpr символьна константа повинна бути присвоєно значення, яке відоме під час компіляції. Наприклад:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
constexpr int c2 = n+7; // Error: we don’t know the value of c2
// ...
}
Для обробки випадків, коли значення "змінної", ініціалізованої зі значенням, яке не відомо на час компіляції, але ніколи не змінюється після ініціалізації, C ++ пропонує другу форму константи ( const ). Наприклад:
constexpr int max = 100;
void use(int n)
{
constexpr int c1 = max+7; // OK: c1 is 107
const int c2 = n+7; // OK, but don’t try to change the value of c2
// ...
c2 = 7; // error: c2 is a const
}
Такі " змінні const " дуже поширені з двох причин:
Довідка: "Програмування: принципи та практика використання C ++" від Stroustrup