简体   繁体   English

检测悬空引用临时

[英]Detect dangling references to temporary

Clang 3.9 extremely reuses memory used by temporaries. Clang 3.9非常重用临时使用的内存。

This code is UB (simplified code): 此代码是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
}

We have tons of code something like above ( my_optional is just a simple example to illustrate it). 我们有大量类似上面的代码( my_optional只是一个简单的例子来说明它)。

Because of UB all clang compiler since 3.9 starts to reuse this memory, and it is lawful behavior. 因为UB所有的clang编译器从3.9开始重用这个内存,而且它是合法的行为。

The question is: how to detect such dangling references at compile time or with something like sanitizer at runtime? 问题是:如何在编译时检测这种悬空引用,或者在运行时检测清理器之类的东西? No clang sanitizer can detect them. 没有clang消毒剂可以检测到它们。

Upd. UPD。 Please do not answer: "use std::optional ". 请不要回答:“使用std::optional ”。 Read carefully: question is NOT about it. 仔细阅读:问题不是关于它。
Upd2. UPD2。 Please do not answer: "your code design is bad". 请不要回答:“你的代码设计很糟糕”。 Read carefully: question is NOT about code design. 仔细阅读:问题不是代码设计。

You can detect misuses of this particular API by adding an additional overload: 您可以通过添加额外的重载来检测此特定API的滥用:

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

If the argument given to get_or_default is a true rvalue, it will be chosen instead, so compilation will fail. 如果给get_or_default赋予的参数是真正的右值,则会选择它,因此编译将失败。

As for detecting such errors at runtime, try using Clang's AddressSanitizer with use-after-return ( ASAN_OPTIONS=detect_stack_use_after_return=1 ) and/or use-after-scope ( -fsanitize-address-use-after-scope ) detection enabled. 至于在运行时检测此类错误,请尝试使用Clang的AddressSanitizer,并使用after-after-return( ASAN_OPTIONS=detect_stack_use_after_return=1 )和/或use-after-scope( -fsanitize-address-use-after-scope )检测。

That is an interesting question. 这是一个有趣的问题。 The actual cause of the dangling ref is that you use an rvalue reference as if it was an lvalue one. 悬挂引用的实际原因是你使用右值引用就好像它是一个左值引用。

If you have not too much of that code, you can try to throw an exception that way: 如果您没有太多的代码,您可以尝试以这种方式抛出异常:

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;
    }
};

That way, if you pass it a ref to a temporary (which is indeed an rvalue), you will get an exception, that you will be able to catch or at least will give a soon abort. 这样,如果你把一个ref传递给一个临时的(确实是一个rvalue),你会得到一个例外,你将能够捕获或至少会很快中止。

Alternatively, you could try a simple fix by forcing to return a temporary value (and not a ref) if you were passed an rvalue: 或者,如果传递了右值,则可以通过强制返回临时值(而不是ref)来尝试一个简单的修复:

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;
    }
};

Another possibility would be to hack the Clang compiler to ask it to detect whether the method is passed an lvalue or an rvalue, by I am not enough used to those techniques... 另一种可能性是破解Clang编译器要求它检测方法是否通过左值或右值, 不习惯那些技术......

You could try out lvalue_ref wrapper from Explicit library. 您可以从Explicit库中尝试lvalue_ref包装器。 It prevents the unwanted binding to a temporary in one declaration, like: 它可以防止在一个声明中对临时的不必要的绑定,例如:

const T& get_or_default(lvalue_ref<const T> def)
{
    return has ? value : def.get();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM