Czy istnieje różnica między następującymi definicjami?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Jeśli nie, jaki styl jest preferowany w C ++ 11?
Czy istnieje różnica między następującymi definicjami?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Jeśli nie, jaki styl jest preferowany w C ++ 11?
Odpowiedzi:
Wierzę, że jest różnica. Zmieńmy ich nazwy, abyśmy mogli łatwiej o nich mówić:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Zarówno PI1
i PI2
są stałe, co oznacza, że nie można ich modyfikować. Jednak tylko PI2
stała czasowa kompilacji. To powinno być inicjowane w czasie kompilacji. PI1
może być zainicjowany w czasie kompilacji lub w czasie wykonywania. Ponadto, tylko PI2
mogą być stosowane w kontekście, który wymaga stałego kompilacji. Na przykład:
constexpr double PI3 = PI1; // error
ale:
constexpr double PI3 = PI2; // ok
i:
static_assert(PI1 == 3.141592653589793, ""); // error
ale:
static_assert(PI2 == 3.141592653589793, ""); // ok
Do czego powinieneś użyć? Używaj w zależności od potrzeb. Czy chcesz mieć pewność, że masz stałą czasową kompilacji, której można używać w kontekstach, w których wymagana jest stała czasowa kompilacji? Czy chcesz mieć możliwość zainicjowania go za pomocą obliczeń wykonanych w czasie wykonywania? Itp.
const int N = 10; char a[N];
działa, a granice tablic muszą być stałymi w czasie kompilacji.
PI1
na stałą całkowania w czasie kompilacji do użycia w tablicy, ale nie do użycia jako parametr szablonu całki nietypowej. Tak więc PI1
wydaje mi się, że konwertowanie na typ całkowy w czasie jest hitem.
enum
inicjalizatora są jedynymi dwiema znaczącymi różnicami pomiędzy const
i constexpr
(i żadna z nich i tak nie działa double
).
1 / PI1
i 1 / PI2
mogą dawać różne wyniki. Nie sądzę jednak, aby ta technika była tak ważna jak rada w tej odpowiedzi.
constexpr double PI3 = PI1;
działa dla mnie poprawnie. (MSVS2013 CTP). Co ja robię źle?
Nie ma tutaj różnicy, ale ma to znaczenie, gdy masz typ z konstruktorem.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
jest stały, ale nie obiecuje, że zostanie zainicjowany w czasie kompilacji. s1
jest oznaczony constexpr
, więc jest stałą, a ponieważ S
konstruktor jest również oznaczony constexpr
, zostanie zainicjowany w czasie kompilacji.
W większości przypadków ma to znaczenie, gdy inicjalizacja w środowisku wykonawczym byłaby czasochłonna i chcesz przekazać tę pracę kompilatorowi, gdzie jest to również czasochłonne, ale nie spowalnia czasu wykonywania skompilowanego programu
constexpr
że doprowadziłoby to do diagnozy, gdyby obliczenie obiektu w czasie kompilacji było niemożliwe. Mniej jasne jest to, czy funkcja oczekująca stałego parametru może być wykonana w czasie kompilacji, czy parametr powinien być zadeklarowany jako, const
a nie jako constexpr
: tzn. Czy constexpr int foo(S)
byłby wykonywany w czasie kompilacji, jeśli wywołam foo(s0)
?
foo(s0)
byłby wykonywany w czasie kompilacji, ale nigdy nie wiadomo: kompilator może wykonywać takie optymalizacje. Z pewnością ani gcc 4.7.2, ani clang 3.2 nie pozwalają mi na kompilacjęconstexpr a = foo(s0);
constexpr wskazuje wartość, która jest stała i znana podczas kompilacji.
const wskazuje wartość, która jest tylko stała; wiedza nie jest obowiązkowa podczas kompilacji.
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
Zauważ, że const nie daje takiej samej gwarancji jak constexpr, ponieważ obiekty const nie muszą być inicjowane wartościami znanymi podczas kompilacji.
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
Wszystkie obiekty constexpr są const, ale nie wszystkie obiekty constexpr są constexpr.
Jeśli chcesz, aby kompilatory gwarantowały, że zmienna ma wartość, której można użyć w kontekstach wymagających stałych czasowych kompilacji, narzędziem, do którego należy sięgnąć, jest constexpr, a nie const.
Constexpr stałe symboliczne należy podać wartość, która jest znany w czasie kompilacji. Na przykład:
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
// ...
}
Aby obsłużyć przypadki, w których wartość „zmiennej”, która jest inicjowana wartością nieznaną w czasie kompilacji, ale nigdy się nie zmienia po inicjalizacji, C ++ oferuje drugą formę stałej ( const ). Na przykład:
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
}
Takie „ stałe zmienne” są bardzo powszechne z dwóch powodów:
Odniesienie: „Programowanie: zasady i praktyka przy użyciu C ++” Stroustrup