Обнаружить висячие ссылки на временные

Clang 3.9 чрезвычайно многократно использует память, используемую временными.

Этот код UB (упрощенный код):

template <class T>
class my_optional
{
public:
bool has{ false };
T value;

const T& get_or_default(const T& def)
{
return has ? value : def;
}
};

void use(const std::string& s)
{
// ...
}

int main()
{
my_optional<std::string> m;
// ...
const std::string& s = m.get_or_default("default value");
use(s); // s is dangling if default returned
}

У нас есть тонны кода примерно так (my_optional это просто простой пример, чтобы проиллюстрировать это).

Из-за UB весь clang компилятор начиная с 3.9 начинает повторно использовать эту память, и это законное поведение.

Вопрос в том, как обнаружить такие свисающие ссылки во время компиляции или с чем-то вроде sanitizer во время выполнения? Никакое дезинфицирующее средство не может обнаружить их.

Upd. Пожалуйста, не отвечайте: «используйте std::optional«. Читайте внимательно: вопрос не об этом.
UPD2. Пожалуйста, не отвечайте: «Ваш код плохой». Читайте внимательно: вопрос НЕ в дизайне кода.

20

Решение

Вы можете обнаружить злоупотребления этим конкретным API, добавив дополнительную перегрузку:

const T& get_or_default(T&& rvalue) = delete;

Если аргумент дан get_or_default является истинным значением, оно будет выбрано, поэтому компиляция не удастся.

Что касается обнаружения таких ошибок во время выполнения, попробуйте использовать AddressSanitizer Clang с use-after-return (ASAN_OPTIONS=detect_stack_use_after_return=1) и / или использование после объема (-fsanitize-address-use-after-scope) обнаружение включено.

23

Другие решения

Это интересный вопрос. Фактическая причина висячего ссылки заключается в том, что вы используете ссылку rvalue, как если бы она была ссылкой lvalue.

Если у вас не слишком много этого кода, вы можете попытаться создать исключение следующим образом:

class my_optional
{
public:
bool has{ false };
T value;

const T& get_or_default(const T&& def)
{
throw std::invalid_argument("Received a rvalue");
}

const T& get_or_default(const T& def)
{
return has ? value : def;
}
};

Таким образом, если вы передадите ему ссылку на временное значение (которое на самом деле является значением), вы получите исключение, которое вы сможете перехватить или, по крайней мере, скоро прекратите.

В качестве альтернативы, вы можете попробовать простое исправление, принудительно возвращая временное значение (а не ссылку), если вам было передано значение rvalue:

class my_optional
{
public:
bool has{ false };
T value;

const T get_or_default(const T&& def)
{
return get_or_default(static_cast<const T&>(def));
}

const T& get_or_default(const T& def)
{
return has ? value : def;
}
};

Другой возможностью было бы взломать компилятор Clang и попросить его определить, передается ли метод lvalue или rvalue, я Я недостаточно привык к этим методам …

3

Вы можете попробовать lvalue_ref обертка от Явный библиотека. Это предотвращает нежелательную привязку к временному в одном объявлении, например:

const T& get_or_default(lvalue_ref<const T> def)
{
return has ? value : def.get();
}
3
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector