TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #983 4.82K
​​WAT
#опытным

Спасибо, ₿ Satoshic, за любезно предоставленный примерчик в рамках рубрики #ЧЗХ.

Можно ли сравнить одинаковые объекты и получить результат, что они не равны? В С++ можно все.

Делаем вот так:

constexpr std::array array = {"I", "love", "C++"};

int main() {
if (auto iter = std::ranges::find(array, "C++"); iter == std::end(array)) {
assert(false && "comptime arg");
}
// let's go with runtime now
if (setenv("RUNTIME", array[2], 0) != 0) {
assert(false && "setenv");
}
char *runtime_str = getenv("RUNTIME");
assert(strcmp(runtime_str, array[2]) == 0 && "equal strings");

if (auto magick_iter = std::ranges::find(array, runtime_str);
magick_iter == std::end(array)) {
assert(false && "runtime arg");
}
}


Определяем массив строк и в начале ищем в нем элемент, значение которого известно на момент компиляции.

Дальше определяем переменную окружения RUNTIME со значением третьего элемента массива.

После получаем значение этой переменной и сравниваем ее с оригиналом.

Ну и в конце ищем среди массива строку эту runtime_str.

Казалось бы, никакие ассерты не должны выстрелить. Мы просто занимается типичной программерской работой - перекладываем одно и то же значение в разные места и сравниваем. Одинаковые объекты должны быть равны.

Но нет! Не равны. Программа зафейлится с ассертом "Assertion `false && "runtime arg"' failed."

WAT?! Почему мы не можем найти строку "С++" в массиве, если она там очевидно есть?

Дьявол кроется в деталях.

Вспоминаем школьную математеку CTAD. Какой тип элементов массива выведется?

Правильно, const char *.

А как std::ranges::find сравнивает такие элементы?

Правильно, по правилам сравнения указателей. Не по содержимому объектов, а по их адресам. Если адреса одинаковые, то два указателя равны. Нет - не равны.

Первый ассерт не сработал, потому что в массиве и при поиске стоит один и тот же строковый литерал "C++", на место которого компилятор подставит один и тот же адрес.

Второй ассерт не срабатывает, потому что мы явно сравниваем сишные строки через strcmp, то есть их содержимое.

А вот последний ассерт просто говорит о том, что указатель runtime_str не был найден в массиве, потому что там нет такого адреса.

И это нормально, ведь когда мы получаем указатель на значение переменной окружения - этот указатель указывает на динамически выделенную память в окружении процесса. А литерал "С++" указывает на секцию read-only данных.

В общем, суть в том, что эти указатели имеют просто разные адреса, поэтому они и не одинаковы.

Так что аккуратно используйте CTAD с сишными строками, может привести к интереснейшему каскаду удивительнейших багов.

Express your wishes precisely. Stay cool.

#cppcore #cpp17
  • ❤ 23
  • 👍 12
  • 😁 7
  • 🔥 5
  • 👎 1
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →