Следуя документации 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. Как преодолевается эта проблема?
Как преодолевается эта проблема? Это не так: Таким образом, std::launder() не решает проблему, позволяющую избежать неопределенного поведения в контейнерах на основе распределителя при наличии элементов с постоянными/ссылочными членами. Реализация просто знает, что этого не будет. любая проблема, поскольку они имеют контроль и знают, что константный объект все еще не будет там.
Ни один инструмент не выявит все случаи НБ.
Это устарело. Начиная с C++20, размещение new структуры, содержащей константную переменную, больше не является UB. Однако выполнение дополнительных отправок не удастся, поскольку необходимые функции-члены для перераспределения будут удалены.
Во-первых, это поведение не определено стандартом (вплоть до 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
ubsan проверяет множество возможных случаев неопределенного поведения. Он не обещает найти все возможные UB.
Кстати, у ubsan есть -fsanitize=vptr
, и в этом случае P0532R0 говорит, что GCC также попытается оптимизировать. Так что, возможно, вы сможете получить рабочий пример, используя его вместо константного члена.
внутреннее использование размещения новых причин UB
Если компилятор использует размещение new для перезаписи первого элемента вектора, это больше не UB, начиная с C++ 20. Размещение new позволяет «прозрачно заменять» константные значения, если оно не является константой верхнего уровня. Кроме того, для доступа больше не требуется std::launder
. Подробную информацию о замене прозрачного материала смотрите в Basic Life.
Однако оператор копирования копирования автоматически удаляется, поэтому размер векторного объекта больше нельзя изменить, например, путем добавления дополнительных элементов, которые превышают емкость вектора.
См. это обсуждение о том, как написать собственный оператор присваивания копирования для обработки классов с константными или ссылочными объектами.
Спасибо за ответ. В нашем случае применяется 8.3? o 1 не является полным константным объектом
Да, o1 не является полным константным объектом. Итак, 8.3 применим. Было бы так, если бы std::vector<const X> v;
однако это не разрешено, поскольку распределитель вектора требует, чтобы объект не был константным. Однако это может быть объект, содержащий константу.
Причин может быть много. Только поставщик компилятора может сказать наверняка. Вы можете пометить gcc и сообщить об этом на gnu.gcc, поскольку вы уже знаете, что это UB.