TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #206 2.3K
​Почему не работает?

При подготовке постов увидел один вопрос на стековерфлоу и решил его разобрать здесь. Не рокет сайенс, но кому-то будет интересно чуть-чуть подумать и поразмышлять.

Что произойдет при попытке компиляции и запуска следующего кода?

#include <type_traits>

template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && std::forward<decltype(args)>(args));
}

template<typename... Args>
constexpr bool AndR(Args&&... args)
{
return (std::forward<decltype(args)>(args) && ...);
}

int main()
{
bool* pb = nullptr;
false && (*pb = true);
AndR(false, (*pb = true));
AndL(false, (*pb = true));
}


Ответ не то, чтобы сильно интригующий, но все же оставлю его под спойлерами, чтобы была возможно подумать, а не сразу ответ читать.

Компиляция завершится успешно, но при выполнении будет сегфолт. На вот этой строчке "AndR(false, (*pb = true));".

Коротко разберем происходящее. Допустим я хочу проверить короткосхемность логического И во всех ипостасях. То есть в обычном виде и в виде fold expression. Для fold выражения нужны функции шаблонные. Вот я и определяю 2 штуки для правой и левой свертки. Ну и есть использование оператора в обычном виде.

Проверять короткосхемность я буду с помощью записи по нулевому указателю. Мы все знаем, что аяаяй, так делать нельзя, но в учебных целях для понимания происходящего - можно.

Так вот. Свойство short circuit дает операции право не вычислять следующие аргументы после того, как ответ уже будет известен. В нашем случае, первый аргумент - false и по идее дальше вычислений быть не должно.

Так и делается во второй строчке функции main. Но вот все падает на 3-й.

Хотя казалось бы должно упасть на 4-й, потому что там так фолд раскрывается, что первым будет учтен последний аргумент. Поэтому первый false нас не защитит от разыменования нулевого указателя.


На 4-й бы тоже упало, если бы не было третьей строчки. Но не по той причине, которую я указал.

Дело в том, что вызов функции - это не вызов оператора напрямую. Если для оператора разрешена короткосхемность, то аргументы вычисляются по порядку по мере выполнения оператора. Для функций же их аргументы обязаны быть вычислены до входа в функцию. Поэтому мы даже не входим в функцию, а падаем при вычислении аргумента - присваивании по нулевому указателю.

Теперь, чтобы старшим ребятам не совсем скучно было, задам вопрос. Как сделать так, чтобы все-таки не упасть в функции, но в какой-то форме передать такие же параметры в качестве аргументов? Отписывайте в комментах свои варианты.


Train your brain. Stay cool.

#template #cpp17
  • ❤ 8
  • 👍 6
  • 🔥 3
More from @grokaemcpp
  1. Oct 17, 2026​​Оптимистичные и пессимистичные блокировки #опытным Концепция блокировок уходит далеко за…
  2. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  3. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  4. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  5. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  6. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
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 →