TGViewer
C++: Хроники Дурки🚑 C++: Хроники Дурки🚑 @cpp_durka · 916 subscribers
Post #56 2.07K
Есть вот такая шляпа:


struct Buffer {
std::vector<int> data{1, 2, 3};

const std::vector<int>& Items() const {
return data;
}
};

Buffer MakeBuffer() {
return {};
}

int main() {
for (int x : MakeBuffer().Items()) {
std::cout << x << ' ';
}
std::cout << '\n';
}


Что будет выведено?

Если запускать gcc/clang с


-fsanitize=address -fsanitize=undefined


То они взворвуться всякими ошибками на доступ к памяти.


==1==ERROR: AddressSanitizer: stack-use-after-scope on address 0x6cc5bdef0060 at pc 0x5806274e084a bp 0x7ffc913eefb0 sp 0x7ffc913eefa8
READ of size 8 at 0x6cc5bdef0060 thread T0
#0 0x5806274e0849 in __gnu_cxx::__normal_iterator<int const*, std::vector<int, std::allocator<int>>>::__normal_iterator(int const* const&) /opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/16.0.1/../../../../include/c++/16.0.1/bits/stl_iterator.h:1059:20



Без них

clang выводит:

692359176 -2055096448 -2055096448


icc выводит:

163141 0 -143200117


gcc и msvc не выводит ничего....


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

А под капотом - так называемый "13-летний баг", историю которого можно отследить вот по этим ссылкам:
https://cplusplus.github.io/CWG/issues/900.html
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2012r2.pdf
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2644r1.pdf
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2718r0.html

Какие выводы мы можем сделать по итогу этих ссылок? Что в С++23 починили таки эту проблему.

Содержательно fix такой: в [class.temporary] добавили четвёртый специальный контекст, в котором временный объект живёт дольше обычного — если он создан в for-range-initializer range-based for, то его lifetime продлевается на жизнь скрытой ссылки, то есть фактически на весь цикл.


The fourth context is when a temporary object other than a function parameter object is created in the for-range-initializer of a range-based for statement. If such a temporary object would otherwise be destroyed at the end of the for-range-initializer full-expression, the object persists for the lifetime of the reference initialized by the for-range-initializer.


И оно поддержано уже в gcc/clang (но не других компиляторах).

Пример с теми же санитайзерами выводит:


1 2 3


Приятно, что всего через 13 лет один из самых болезненных багов из С++ ушел...
godbolt.org Compiler Explorer - C++ struct Buffer { std::vector<int> data{1, 2, 3}; const std::vector<int>& Items() const { return data; } }; Buffer MakeBuffer() { return {}; } int main() { for (int x : MakeBuffer().Items()) { std::cout << x << ' '; …
  • 🔥 24
  • 😭 6
  • 👍 3
  • ❤ 1
More from @cpp_durka
  1. Sep 8, 2026Мошенники заставили пенсионерку из Москвы переписать квартиру на Rust
  2. Aug 31, 2026Всяко разное болезненное есть в С++, из всего болезненного одно из самых болезненных - это…
  3. Aug 24, 2026Если кто не знаком с библиотекой nlohmann/json - она прекрасна. Мои мысли о том, как должн…
  4. Aug 21, 2026#толькосвоимемы Код взят тут. if (dim == 0) idx_dim = i0; else if (dim == 1) idx_dim = i1;…
  5. Aug 17, 2026Ладно, искать баги в компиляторах весело, но недостаточно. Давайте поиграемся в чуть более…
  6. Aug 10, 2026Ладно, разумеется прошлый пост был набросом. Никогда не обновляйте компиляторы без очень с…
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 →