Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perfect forwarding for returned value?

I'm overloading an operator[]:

const Type&& operator[](int index) const {
        if (index >= size) { std::cout << "Error: excessive index.\n"; return 0; }
        else if (index < 0) { std::cout << "Error: negative index.\n"; return 0; }
        else {
            Node* temp = head->next;
            for (int i = 0; i < index; i++) { temp = temp->next; }
            return temp->value;
        }
    }

But I need a duplicate of it, which will return non-const Type value. I read that we can use perfect forwarding for cases when arguments of function can be both const or non-const (so that we wrap them in forward<Type> every time we use it), but how to use it for value that is returned?

Also, if I simply want to return nothing, should I write return 0; or return NULL;? Which is more understandable?

like image 864
Martian Avatar asked May 21 '20 10:05

Martian


1 Answers

Such unified syntax that would work for all, const/volatile/non-const/lvalue/rvalue/etc., implicit object parameters is currently not supported. However, there's the proposal P0847r4: Deducing this which adds this functionality. With that, you could say:

template <typename Self>
auto&& operator[](this Self&& self, int index)
{
    if (index >= self.size) { throw std::out_of_range("Error: excessive index"); }
    else if (index < 0) { throw std::out_of_range("Error: negative index"); }

    auto* temp = self.head;
    for (int i = 0; i < index; i++) { temp = temp->next; }        
    return std::forward_like<Self>(temp->value);
}

Until it becomes available, the best what you can do is to shorten the implementation for const and non-const overloads, and delegate both calls to a static helper function template, that actually can deduce the cv-qualification and value category of the implicit object parameter:

class List
{
private:
    template <typename Self>
    static auto&& get(Self&& self, int index)
    {    
        if (index >= self.size) { throw std::out_of_range("Error: excessive index"); }
        else if (index < 0) { throw std::out_of_range("Error: negative index"); }

        Node* temp = self.head;
        for (int i = 0; i < index; i++) { temp = temp->next; }
        return temp->value;
    }

public:
    const Type& operator[](int index) const
    {
        return get(*this, index);
    }

    Type& operator[](int index)
    {
        return get(*this, index);
    }

private:
    // ...
};

DEMO

Also, note that the idiomatic approach for a function returning a reference is to throw an exception in case nothing can be returned, or to insert ad-hoc and return a new object.

like image 53
Piotr Skotnicki Avatar answered Sep 22 '22 04:09

Piotr Skotnicki