TGViewer
this->notes. this->notes. @thisnotes · 4.52K subscribers
Post #222 2.01K
#cpp

Два факта про неочевидные (и к счастью редкие) кеки C++.

Первое про union.

Важно помнить, что у union в C++ могут быть конструкторы/деструкторы, потому что важно уметь выбирать кого вы конструируете по умолчанию, и соответственно уметь вызывать корректный деструктор в зависимости от активного члена. И вот если у вас объектам надо вызывать конструктор, и вы union’у его не реализовываете, то код просто не компилируется, потому что дефолтный по умолчанию удалён. Но это лирическое отступление. Имеем такой union:

union U {
std::string s_;
std::vector<int> v_;
U(std::string s) { new (&s_) std::string(s); }
U(std::vector<int> v) { new (&v_) std::vector<int>(v); }
~U() {}
};


И собственно сам кек. Т.к. по дефолту деструктор удалён, то в данном случае = default для него означает, что деструктора нет. Т.е. поведение при пустом кастомном деструкторе и при ~U() = default отличается.
Вы спросите, а почему в деструкторе ничего не делаем? Я отвечу: потому что этим должен заниматься пользователь union снаружи. Он знает, какой член является активным. Он пусть с этим и разбирается.
Ну и чтобы соблюсти формальности, уточним, что вызывать деструктор std::string не как

s_.~string()

а как

s_.~basic_string<char>()

потому что это же алиас.

Второе про function-try-block.

В C++ существует альтернативный синтаксис для определения тела функции, позволяющий навесить на него целиком перехват и обработку исключений:

// Стандартный способ
void f() {
try {
may_throw();
} catch (...) {
handle_error();
}
}

// Альтернативный синтаксис
void f() try {
may_throw();
} catch (...) {
handle_error();
}


Эта фича позволяет нам ловить исключения из списка инициализации в конструкторах:

struct S {
A a_;
B b_;

S(A a, B b) try : a_(a), b_(b) {
} catch (…) {
// handle
}
};


Правда тут есть один опасный момент. После обработки ошибки в catch ошибка будет неявно проброшена выше. Это в целом логично: если при инициализации полей класса вылетело исключение, мы никак не можем исправить ситуацию и починить объект.

А что с деструкторами? Из них же крайне нежелательно бросать исключения, потому словить всё было бы удобно. Но тут пранк — поведение такое же, как и в конструкторах. Т.е. catch из function-try-block неявно пробрасывает исключение выше. Вы только что нарушили неявный noexcept(true). Но в отличие от конструкторов, для деструкторов есть мегакостыль: просто добавьте return!

struct A {
~A() try {
throw std::runtime_error("err");
} catch (…) {
std::cout << “ERROR”;
return; // исключение не будет перевыброшено!
}
};


Таким образом словили кейс, когда return в конце void-функции меняет её поведение.

На всякий напомню, что ловить исключения через catch (…) не очень хорошо. Вообще можно почитать мою статью про обработку ошибок.

Ну а плюсы что. Кринж.
  • 👍 13
  • 😱 5
  • 😁 3
  • ❤ 1
More from @thisnotes
  1. Sep 17, 2026#common Сидите вы себе спокойно, разрабатываете поиск каких-нибудь объектов. Может это тов…
  2. Sep 9, 2026#cpp #books Да, книга 2001ого года. Мы ровесники. И да, в ней в основном обсуждаются какие…
  3. Sep 2, 2026#perf Попробовал собрать в кучку (кажется, немного сумбурно всё же) мысли по двум моментам…
  4. Aug 31, 2026Давайте новый тег заведём: #perf Во-первых, надо понять, что я вообще понимаю под перфом,…
  5. Aug 27, 2026#common Мы часто делаем системы, которые обладают какими-то ограничениями. Ограничения наш…
  6. Aug 24, 2026#list 0. [talk] Achieving Peak Performance for Matrix Multiplication in C++. Aliaksei Sala…
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 →