Почему 0 == ("abcde" +1) не является константным выражением?

Почему следующий код не компилируется?

// source.cpp

int main()
{
   constexpr bool result = (0 == ("abcde"+1));
}

Команда компиляции:

$ g++ -std=c++14 -c source.cpp

Выход:

source.cpp: In function ‘int main():
source.cpp:4:32: error: ‘((((const char*)"abcde") + 1u) == 0u) is not a constant expression
 constexpr bool result = (0 == ("abcde"+1));
                         ~~~^~~~~~~~~~~~~~~

Я использую gcc6.4.

Ответ 1

Ограничения на то, что можно использовать в константном выражении, определяются в основном как список негативов. Есть куча вещей, которые вы не можете оценить ([expr.const]/2 в C+ +1 4) и некоторые вещи, значения которых должны быть получены ([expr.const]/4 в C+ +1 4). Этот список меняется от стандарта к стандарту, становясь все более уместным со временем.

В попытке оценить:

constexpr bool result = (0 == ("abcde"+1));

нет ничего, что нам не разрешено оценивать, и у нас нет результатов, которых нам не разрешено иметь. Нет неопределенного поведения и т.д. Это совершенно правильное, если нечетное, выражение. Просто тот, который gcc 6.3 запрещает - это ошибка компилятора. gcc 7+, clang 3. 5+, msvc все скомпилируют.


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

Давайте начнем с объявления вроде:

constexpr char const* p = "abcde";

Этот указатель имеет некоторое значение. Позвольте сказать N Главное, что все, что вы можете сделать, чтобы попытаться наблюдать N во время постоянной оценки, будет плохо сформировано. Вы не можете привести его к целому числу, чтобы прочитать значение. Вы не можете сравнить его с другой, не связанной строкой (с помощью [expr.rel]/4.3):

constexpr char const* q = "hello";
p > q; // ill-formed
p <= q; // ill-formed
p != q; // ok, false

Мы можем с уверенностью сказать, что p != q потому что где бы они ни указывали, они явно разные. Но мы не можем сказать, кто идет первым. Такое сравнение является неопределенным поведением, а неопределенное поведение запрещено в константных выражениях.

Вы действительно можете сравнить только с указателями в том же массиве:

constexpr char const* a = p + 1; // ok
constexpr char const* b = p + 17; // ill-formed
a > p; // ok, true

Везде, где это, что p указывает, мы знаем, что a точки после нее. Но нам не нужно знать N чтобы определить это.

В результате фактическое значение N во время постоянной оценки является более или менее несущественным.

"abcde" это... где-то. "abcde"+1 указывает на единицу позже этого и имеет значение "bcde". Независимо от того, куда он указывает, вы можете сравнить его с нулевым указателем (0 является константой нулевого указателя), и это не нулевой указатель, следовательно, это сравнение оценивается как ложное.

Это совершенно правильно сформированная константная оценка, которую gcc 6.3 случайно отвергает.


Хотя мы просто заявляем с помощью fiat, что std::less()(p, q) предоставляет некоторое значение, которое дает согласованный общий порядок во время компиляции и дает тот же ответ во время выполнения. Что... интересная головоломка.

Ответ 2

GCC 7.x и выше компилирует ваш код без каких-либо ошибок (с -std=c++14 -pedantic-errors). Clang и MSVC также не жалуются.

Я предполагаю, что вы наблюдаете ошибку GCC 6.

Ответ 3

Код полагается на неопределенное поведение. Значение строковой константы не определено во время компиляции. Также не определено, полностью ли оптимизирует компилятор конструкцию или нет.

Выражение "abcde" + 1 может быть адресом в памяти, который будет определен только после загрузки программы во время выполнения. Компоновщик может поместить запись перемещения в двоичный файл, а загрузчик переместит исполняемый файл на его окончательный адрес, назначив адрес строке.

Или компилятор может оптимизировать все выражение целиком, полагая, что сама строковая константа никогда не используется и ее адрес (а также ее адрес + 1) всегда ненулевой.

Вопрос и комментарии показывают, что оба сценария случаются. И оба действительны в С++ 14. Он не определен с языковой точки зрения, поэтому реализации (например, компиляторы) могут демонстрировать любое поведение.

Ответ 4

"abcd" - это указатель const char * на фрагмент памяти, содержащий символ abcd\0. Расположение этого куска памяти не известно во время компиляции. Поэтому значение указателя ("abcde"+1) также не известно во время компиляции. Поэтому все вычисляемое выражение (0 == ("abcde"+1)) не известно во время компиляции и поэтому не может быть constexpr, даже несмотря на то, где будет находиться строка "abcd" поместил значение выражения будет таким же.