Search code examples
c++c++11rvalue-reference

A use-case for r-value members


I've discovered an interesting piece of code and I wonder if it is UB or not? At least, according to cppreference it should not be. Is it a valid case for using an r-value reference?

The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference...

Something similar was discussed in this post, but it does not fully address my case.

template <typename T>
class ResourceLocator {
public:
    static void load(ResourceLocator<T>&& instance) {
        instance_ = std::move(instance);
    }

protected:
    static ResourceLocator<T>&& instance_;
    // static ResourceLocator<T>& instance_; // does not extend lifetime
    // static const ResourceLocator<T>&& instance_; // const is too limiting

Solution

  • TL;DR: This won't work the way you want it to. It likely won't be UB though, as long as instance_ references something that lives long enough to support an operator=(...) call from load(...).

    Explanation

    References of any kind, whether rvalue or lvalue, cannot be rebound once initialized, and cannot be left uninitialized. Fundamentally what you're wanting here cannot work using references.

    The static instance_ will have to have already been initialized to some value on program startup, which would make:

    instance_ = std::move(instance);
    

    assign to the object that instance_ originally references, meaning this uses the assignment operator operator= for the ServiceLocator<T>, rather than rebinding the reference.

    Even if it could work, RValues can only extend lifetimes to the natural scope where its initialized (as if they are object-values), and can only extend the lifetime when initialized with a true temporary object (e.g. a PR-value, such as a T() expression or the result of a function returning a by-value object).

    So even if rebinding was possible, this extension would not apply since the parameters are not producing temporary expressions.

    Possible Alternative

    If you're wanting to create a locator of an object that is immovable/uncopyable, maybe consider either using std::optional or std::unique_ptr to allow for a null-state, and offer an emplace-like function to construct it in-place. Such as:

    template <typename T>
    class ResourceLocator {
    public:
        template <typename...Args>
        static void emplace(Args&&...args) {
            instance_.emplace(std::forward<Args>(args)...);
        }
    private:
        static std::optional<ResourceLocator<T>> instance_;
    };
    

    Or, better yet, just don't use resource locators. They're just singletons pretending to be an abstraction, but they're just as evil.