Является ли адрес локальной переменной constexpr?

В книге Бьярна Страуструпа "Язык программирования C++ (4-е издание)" на с. 267 (Раздел 10.4.5 Выражения констант адреса), он использует пример кода, где адрес локальной переменной устанавливается constexpr переменной constexpr. Я думал, что это выглядит странно, поэтому я попытался запустить пример с g++ версии 7.3.0 и не смог получить те же результаты. Вот его пример кода (дословно сокращенно):

extern char glob;

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

Когда я запускаю это, я получаю:

error: ‘(const char*)(& loc) is not a constant expression

Что-то происходит с g++, о котором я не знаю, или есть что-то еще в примере Бьярне?

Ответ 1

Более ранняя печать книги Бьярна Страуструпа "Язык программирования C++ (4-е издание)" на с. 267 имеет ошибку, изложенную в вопросе OP. Текущие печатные и электронные копии были "исправлены", но в них была внесена другая ошибка, описанная ниже. Теперь это относится к следующему коду:

constexpr const char* p1="asdf";

Это нормально, потому что "asdf" хранится в фиксированной ячейке памяти. В более ранней печати книга ошибается здесь:

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

Однако loc не находится в фиксированной ячейке памяти. это в стеке и будет иметь различные местоположения в зависимости от того, когда он вызывается.

Однако в печати четвертого издания есть еще одна ошибка. Это дословный код из 10.5.4:

int main() {
    constexpr const char* p1 = "asdf";
    constexpr const char* p2 = p1;      // OK
    constexpr const char* p3 = p1+2;    // error:  the compiler does not know the value of p1
}

Это не верно. Компилятор/компоновщик знает значение p1 и может определить значение p1+2 во время соединения. Компилируется просто отлично.

Ответ 2

Похоже, что пример из раздела 10.4.5, представленный в моей печатной копии "Язык программирования C++ (4-е издание)", неверен. И поэтому я пришел к выводу, что адрес локальной переменной не является constexpr.

Пример, по-видимому, был обновлен в некоторых версиях PDF, как показано здесь:

enter image description here

Ответ 3

Этот ответ пытается прояснить, почему адрес локальной переменной не может быть constexpr, анализируя пример для архитектуры x86-64.

Рассмотрим следующую игрушечную функцию print_addr(), которая отображает адрес своей локальной переменной local_var и рекурсивно вызывает себя n раз:

void print_addr(int n) {
   int local_var{};
   std::cout << n << " " << &local_var << '\n';

   if (!n)
      return; // base case

   print_addr(n-1);  // recursive case
}

Вызов print_addr(2) привел к следующему выводу в моей системе x86-64:

2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c

Как видите, соответствующие адреса local_var различны для каждого вызова print_addr(). Вы также можете видеть, что чем глубже вызов функции, тем ниже адрес локальной переменной local_var. Это связано с тем, что на платформе x86-64 размер стека уменьшается (т.е. С более высоких адресов на более низкие).

Для вывода выше, стек вызовов будет выглядеть следующим образом на платформе x86-64:

                |     . . .     |
Highest address ----------------- <-- call to print_addr(2) 
                | print_addr(2) |    
                -----------------
                | print_addr(1) |
                -----------------
                | print_addr(0) | <-- base case, end of recursion
Lowest address  ----------------- Top of the stack

Каждый прямоугольник выше представляет кадр стека для каждого вызова print_addr(). local_var каждого вызова находится в соответствующем кадре стека. Поскольку local_var каждого вызова print_addr() находится в своем собственном (другом) стековом фрейме, адреса local_var различаются.

В заключение, поскольку адрес локальной переменной в функции может не совпадать для каждого вызова функции (т.е. Каждый кадр стека вызовов может быть расположен в другой позиции в памяти), адрес такой переменной может не может быть определено во время компиляции, и поэтому не может быть квалифицировано как constexpr.

Ответ 4

Просто чтобы добавить к другим ответам, которые указали на ошибку, стандарт C++ разрешает указатели constexpr только на объекты со статической продолжительностью хранения, один за другим или nullptr. Смотрите [expr.const/8] конкретно # 8.2;

Стоит отметить, что:

  • строковые литералы имеют статическую длительность хранения:
  • Основываясь на ограничениях в объявлении extern переменных, они по своей природе будут иметь длительность статической памяти или длительность локальной памяти потока.

Следовательно, это действительно:

#include <string>

extern char glob;
std::string boom = "Haha";

void f(char loc) {
    constexpr const char* p1 = &glob;
    constexpr std::string* p2 = nullptr;
    constexpr std::string* p3 = &boom;
}

Ответ 5

dsaaaffffffasddddqwccasdqwdascasc