Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a non-const reference causes a binding reference error

Tags:

c++

I've implemented a double linked list using weak and smart pointers. The program is working but I have doubts about that const in the getPrev signature method. If I put const a the end of the method signature it will cause a binding reference error

error: binding reference of type 'std::weak_ptr<Node<Integer> >&' to 'const std::weak_ptr<Node<Integer> >' discards qualifiers
         return prev;

Wasn't the purpose of that const to mark *this as const ? The return type is non-const for my understanding.

Here is the code, main.cpp:

#include <memory>
#include <iostream>
#include "DoubleLinkedList.h"

class Integer {
private:
    int number;
public:
    Integer(int number) : number(number) {}

    int get() { return number; }

};

int main() {

    DoubleLinkedList<Integer> list;
    list.insert(Integer(1));
    list.insert(Integer(2));
    list.insert(Integer(3));
    list.insert(Integer(4));
    list.insert(Integer(5));


    return 0;
}

DoubleLinkedList.h

#include <memory>
#include <vector>
#include <iostream>

template <typename T>
class Node {
private:
    T data;
    std::weak_ptr<Node> prev;
    std::shared_ptr<Node> next;
public:
    Node(): data(0) {}

    Node(const T &object) : data(object) {};

    T getData() const {
        return data;
    }

    void setData(T data) {
        Node::data = data;
    }

    std::weak_ptr<Node> &getPrev() const {
        return prev;
    }

    void setPrev(const std::weak_ptr<Node> &prev) {
        Node::prev = prev;
    }

    std::shared_ptr<Node> &getNext() {
        return next;
    }

    void setNext(const std::shared_ptr<Node> &next) {
        Node::next = next;
    }
};

template <typename T>
class DoubleLinkedList {
private:
    std::shared_ptr<Node<T>> header;
    std::weak_ptr<Node<T>> trailer;
    int size;
public:

    DoubleLinkedList() : size(0) {}

    void insert(const T &value) {
        auto node = std::make_shared<Node<T>>(value);

        if (size++ == 0) {
            header = node;
        } else {
            auto last = trailer.lock();
            last->getNext() = node;
            node->getPrev() = last;
        }
        trailer = node;
    }


};
like image 621
Antonio Santoro Avatar asked Oct 15 '22 12:10

Antonio Santoro


1 Answers

If you are inside a const method, all the data members are considered const.

That is, inside this function:

std::weak_ptr<Node> &getPrev() const

you can imagine the member variables like this:

const T data;
const std::weak_ptr<Node> prev;
const std::shared_ptr<Node> next;

It should be clear that you cannot return a non-const reference to a const object:

const int x;

int& getX()
{
  return x; // error
}

The reference would allow you to modify x even though it is const, so this is forbidden (formally: a non-const reference cannot bind to a const object).

Inside a const member function of Node, prev is a const std::weak_ptr<Node>, so a std::weak_ptr<Node>& cannot bind to it for the exact same reason.


It appears that within insert you do intend to modify node (by changing its prev value), in which case the getPrev function should not be const (because you intend to modify the object). But this kind of access should probably be reserved for the DoubleLinkedList and not some arbitrary outside user. It then becomes a question of interface design: Which parts of your code are implementation details and how those should be hidden from users? Which parts are the interface that users should interact with (with minimal opportunity for breaking things)?

like image 156
Max Langhof Avatar answered Oct 21 '22 14:10

Max Langhof