C++ Неопределенное поведение при векторном доступе

Следуя документации p0532r0.pdf, следующий фрагмент кода приводит к UB.

#include <iostream>
#include <vector>
struct X {
    const int i;
    X(int _i) : i(_i) {}
    friend std::ostream& operator<< (std::ostream& os, const X& x) {
    return os << x.i;
    };
};
int main(){
    std::vector<X> v;
    v.push_back(X(42));
    v.clear();
    v.push_back(X(77));
    std::cout << v[0]; // undefined behaviour
}

Однако UB не обнаруживается, когда приведенный выше фрагмент кода выполняется с помощью дезинфицирующего средства. демо Почему компилятор не обнаруживает эту ошибку? Помимо этого, на основе P0532R0, когда типом вектора является класс, имеющий константный член, внутреннее использование нового размещения вызывает UB. Как преодолевается эта проблема?

Причин может быть много. Только поставщик компилятора может сказать наверняка. Вы можете пометить gcc и сообщить об этом на gnu.gcc, поскольку вы уже знаете, что это UB.

user12002570 23.07.2024 16:41

Как преодолевается эта проблема? Это не так: Таким образом, std::launder() не решает проблему, позволяющую избежать неопределенного поведения в контейнерах на основе распределителя при наличии элементов с постоянными/ссылочными членами. Реализация просто знает, что этого не будет. любая проблема, поскольку они имеют контроль и знают, что константный объект все еще не будет там.

NathanOliver 23.07.2024 17:00

Ни один инструмент не выявит все случаи НБ.

n. m. could be an AI 23.07.2024 18:52

Это устарело. Начиная с C++20, размещение new структуры, содержащей константную переменную, больше не является UB. Однако выполнение дополнительных отправок не удастся, поскольку необходимые функции-члены для перераспределения будут удалены.

doug 23.07.2024 22:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
101
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Во-первых, это поведение не определено стандартом (вплоть до C++17 включительно), но неявно четко определено в GCC (и везде, начиная с C++20). Из связанного PDF-файла:

AFAIK, gcc в настоящее время не оптимизирует константный/эталонный случай, но оптимизирует часть vptr.

Так что в каком-то смысле обнаружить нечего.

Во-вторых, причина, по которой этот UB не обнаруживается ubsan: он его вообще не проверяет.

В руководстве (для версии 9.5) перечислены варианты поведения, проверенные ubsan здесь , и даны ссылки на эквивалентную clang ubsan документацию.

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

Кстати, в вашей демо-версии используется GCC 9.1. Ничего из этого не меняется в более поздних версиях, включая магистральный AFAICS, но именно поэтому я связал руководство по версии 9.5.

Но на основе P0532R0 способ распределения памяти приводит к UB при использовании const

getsoubl 23.07.2024 17:07

ubsan проверяет множество возможных случаев неопределенного поведения. Он не обещает найти все возможные UB.

Useless 23.07.2024 17:08

Кстати, у ubsan есть -fsanitize=vptr, и в этом случае P0532R0 говорит, что GCC также попытается оптимизировать. Так что, возможно, вы сможете получить рабочий пример, используя его вместо константного члена.

Useless 23.07.2024 17:18
Ответ принят как подходящий

внутреннее использование размещения новых причин UB

Если компилятор использует размещение new для перезаписи первого элемента вектора, это больше не UB, начиная с C++ 20. Размещение new позволяет «прозрачно заменять» константные значения, если оно не является константой верхнего уровня. Кроме того, для доступа больше не требуется std::launder. Подробную информацию о замене прозрачного материала смотрите в Basic Life.

Однако оператор копирования копирования автоматически удаляется, поэтому размер векторного объекта больше нельзя изменить, например, путем добавления дополнительных элементов, которые превышают емкость вектора.

См. это обсуждение о том, как написать собственный оператор присваивания копирования для обработки классов с константными или ссылочными объектами.

Спасибо за ответ. В нашем случае применяется 8.3? o 1 не является полным константным объектом

getsoubl 24.07.2024 08:52

Да, o1 не является полным константным объектом. Итак, 8.3 применим. Было бы так, если бы std::vector<const X> v; однако это не разрешено, поскольку распределитель вектора требует, чтобы объект не был константным. Однако это может быть объект, содержащий константу.

doug 24.07.2024 16:47

Другие вопросы по теме

Язык программирования C Неопределенное поведение
Хорошо ли определено приведение к идентичному макету с константными членами?
Приводит ли наличие вектора, содержащего структуры с неинициализированными членами, к неопределенному поведению?
Является ли вставка в вектор при одновременном доступе к вектору неопределенным поведением?
Является ли UB чтение байтов из malloc() без предварительной их инициализации?
Разный вывод в GCC и Visual Studio
Почему вызов оператора* для пустого std::optional вызывает неопределенное поведение, если я не сразу получаю доступ к возвращаемому значению?
Строгое псевдонимирование первого члена структуры через непрозрачный указатель в C
Оставлен ли логический сдвиг из-за неопределенного поведения размера слова в C#?
Является ли неопределенным поведение передача указателя на несконструированный объектstreambuf в конструктор ostream?