Обнаружить висячие ссылки на временные
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. Пожалуйста, не отвечайте: "Ваш код плохой". Читайте внимательно: вопрос НЕ в дизайне кода.
3 ответа
Вы можете обнаружить злоупотребления этим конкретным 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
) обнаружение включено.
Вы можете попробовать lvalue_ref
обертка из явной библиотеки. Это предотвращает нежелательную привязку к временному в одном объявлении, например:
const T& get_or_default(lvalue_ref<const T> def)
{
return has ? value : def.get();
}
Это интересный вопрос. Фактическая причина висячего ссылки заключается в том, что вы используете ссылку 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, поскольку я недостаточно привык к этим методам...