Константы во время компиляции
Кажется, мне не хватает чего-то довольно фундаментального. Я пытаюсь использовать члены массива const во время компиляции.
const int list[3] = { 2, 5, 7 };
const int a = list[2]; // this doesn't error?
template<int N1, int N2>
struct tmax {
enum { value = ((N1 > N2) ? N1 : N2) };
};
const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // error is here
int main()
{
return 0;
}
Ошибки:
prog.cpp:10:24: error: 'list' cannot appear in a constant-expression
prog.cpp:10:30: error: an array reference cannot appear in a constant-expression
Вот соответствующая ссылка IDEOne
Так почему это не работает? Что мне не хватает? Что я должен делать по-другому?
2 ответа
Просто потому, что объект const
не означает, что это константа времени компиляции.
main.cpp:10:20: error: non-type template argument is not a constant expression
const int c = tmax<list[0],list[1]>::value; // error is here
^~~~~~~
main.cpp:10:20: note: read of non-constexpr variable 'list' is not allowed in a constant expression
main.cpp:1:11: note: declared here
const int list[3] = { 2, 5, 7 };
^
Это причина constexpr
:
constexpr int list[3] = { 2, 5, 7 };
template<int N1, int N2>
struct tmax {
enum { value = ((N1 > N2) ? N1 : N2) };
};
const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // works fine now
Что касается того, почему это работает:
const int a = list[2]; // this doesn't error?
инициализация const
переменная не требует постоянного выражения:
int foo(int n) {
const int a = n; // initializing const var with a non-compile time constant
Выражения не являются константными выражениями, если они содержат одно из нескольких запрещенных подвыражений. Один из таких запрещенных подвыражений:
- преобразование lvalue в rvalue (4.1), если оно не применяется к
- glvalue целочисленного типа или типа перечисления, которое относится к энергонезависимому константному объекту с предшествующей инициализацией, инициализированной с помощью константного выражения, или
- glvalue литерального типа, который ссылается на энергонезависимый объект, определенный с
constexpr
или это относится к подобъекту такого объекта, или- glvalue литерального типа, который относится к энергонезависимому временному объекту, время жизни которого не закончилось, инициализируется постоянным выражением;
В частности, в то время как имя константного объекта типа enum или intergral, инициализированного константным инициализатором, образует константное выражение (чтение его значения является причиной преобразования lvalue в rvalue), подобъекты агрегированного объекта const (такого как как list
в вашем примере массив) нет, но если бы объявили constexpr
,
const int list[3] = { 2, 5, 7 };
const int a = list[2];
Это верно, но a
не является константным выражением, потому что оно не инициализируется константным выражением.
Изменяя декларацию list
(нам не нужно менять декларацию a
), мы можем сделать a
сформировать постоянное выражение.
constexpr int list[3] = { 2, 5, 7 };
const int a = list[2];
Как list[2]
теперь постоянное выражение, a
сейчас const
объект интегрального типа, инициализированный с постоянным выражением так a
теперь можно использовать как константное выражение.