#опытным
Спасибо, ₿ 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?! Почему мы не можем найти строку "С++" в массиве, если она там очевидно есть?
Дьявол кроется в деталях.
Вспоминаем
Правильно, const char *.
А как std::ranges::find сравнивает такие элементы?
Правильно, по правилам сравнения указателей. Не по содержимому объектов, а по их адресам. Если адреса одинаковые, то два указателя равны. Нет - не равны.
Первый ассерт не сработал, потому что в массиве и при поиске стоит один и тот же строковый литерал "C++", на место которого компилятор подставит один и тот же адрес.
Второй ассерт не срабатывает, потому что мы явно сравниваем сишные строки через strcmp, то есть их содержимое.
А вот последний ассерт просто говорит о том, что указатель
runtime_str не был найден в массиве, потому что там нет такого адреса.И это нормально, ведь когда мы получаем указатель на значение переменной окружения - этот указатель указывает на динамически выделенную память в окружении процесса. А литерал "С++" указывает на секцию read-only данных.
В общем, суть в том, что эти указатели имеют просто разные адреса, поэтому они и не одинаковы.
Так что аккуратно используйте CTAD с сишными строками, может привести к интереснейшему каскаду удивительнейших багов.
Express your wishes precisely. Stay cool.
#cppcore #cpp17