2

Let's say I have a class like this:

class LinkedList
{
    struct Node
    {
        int StoredValue;
        // ...
    };

    Node& GetNodeReference(std::size_t Index)
    {
        // ...

        return NodeReference;
    }

    public:

    int Get(std::size_t Index) const
    {
        return GetNodeReference(Index).StoredValue;
    }
};

This won't compile because the const method Get uses GetNodeReference, which cannot be const because it returns a reference.

How can I work around this?

Maxpm
  • 24,113
  • 33
  • 111
  • 170
  • Do you control the code? Could you make GetNodeReference return a const Node&? – Jeff Foster Feb 25 '11 at 14:46
  • @Jeff The idea is that I can use `GetNodeReference` for my setter as well as my getter, so the reference needs to be changeable. – Maxpm Feb 25 '11 at 14:52
  • 2
    In that case, the usual "overload on const" approach, as demonstrated in my answer, should work. – fredoverflow Feb 25 '11 at 14:54
  • like an option, it should be possible to fix it declaring "int StoredValue" as "mutable int StoredValue", this should allow modifying value from const method. Am I right? I've read it from here http://stackoverflow.com/questions/105014/does-the-mutable-keyword-have-any-purpose-other-than-allowing-the-variable-to – Tebe Apr 22 '13 at 19:01

4 Answers4

8

I'm not sure what you're trying to achieve, but you could provide two overloads of GetNodeReference:

Node& GetNodeReference(std::size_t Index)
{
    // ...

    return NodeReference;
}

const Node& GetNodeReference(std::size_t Index) const
{
    // ...

    return NodeReference;
}

Note that the second overload has two const modifers, one at the beginning of the line for the return type and one at the end of the line for the implicitly passed *this object.

To avoid code repetition, you can implement the non-const overload based on the const overload:

const Node& GetNodeReference(std::size_t Index) const
{
    // ...

    return NodeReference;
}

Node& GetNodeReference(std::size_t Index)
{
    return const_cast<Node&>(static_cast<const LinkedList&>(*this).getNodeReference(Index));
}

This technique is discussed in Item 3 of Effective C++ by Scott Meyers.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • `GetNodeReference` is a private function. Is it necessary to write pair-functions? – Nawaz Feb 25 '11 at 15:00
  • If there is some code to produce the NodeReference, and that can be done in a const function, you can just refactor it to a separate function called from both overloads. To me it seems that the "avoid repetition trick" produces more source code rather than less. :-) – Bo Persson Feb 25 '11 at 16:03
3

Don't implement an indexed Get function for lists at all. It will be WAY too easy for a new developer to come in and use it in a loop, turning a linear interation into a polynomial iteration.

If you need such a capability, make a free-function that uses the builtin iterators of the list in conjunction with say std::advance to get the node you want.

If you absolutely need the member function, then the normal approach is the one suggested by @FredOverflow and use overloads (quote his code):

const Node& GetNodeReference(std::size_t Index) const
{
    // ...

    return NodeReference;
}

Node& GetNodeReference(std::size_t Index)
{
    return const_cast<Node&>(static_cast<const LinkedList&>(*this).getNodeReference(Index));
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
1

How about this?

class LinkedList {

private:

struct Node
{
    int StoredValue;
    // ...
};

Node NodeReference;

const Node* const GetNodeReference(std::size_t Index) const
{
    return &NodeReference;
}

public:

int Get(std::size_t Index) const
{
    const Node *const node = GetNodeReference(Index);
    return node->StoredValue;
}

};

Edit:

As you can read in the comments

const Node* const GetNodeReference(std::size_t Index) const () ...

should be changed to:

const Node* GetNodeReference(std::size_t Index) const () ...

AudioDroid
  • 2,292
  • 2
  • 20
  • 31
  • I'm having trouble interpreting the signature. What does the `const` immediately after `Node*` mean? – Maxpm Feb 25 '11 at 15:15
  • 1
    It means that neither the pointer (nor the content (first "const")) shall be changed. – AudioDroid Feb 25 '11 at 15:19
  • @Max: In this context, it means nothing, because scalar return types cannot be `const`. The compiler ignores it. – fredoverflow Feb 25 '11 at 15:20
  • Actually this could be done nicer, I put it in another answer though, otherwise the above question wouldn't make sense any more. – AudioDroid Feb 25 '11 at 15:25
  • @FredOverflow: I don't know about your compiler, but if I try "node->StoredValue++;" I get a compiler error: "increment of data-member `LinkedList::Node::StoredValue' in read-only structure, which I believe is prove, that the first "const" is not ignored by my compiler. – AudioDroid Feb 25 '11 at 15:44
  • @FredOverflow: If I try "node = 0;" my compiler says: "assignment of read-only variable `node'" which I believe to be prove, that the second const is not being ignored by my comiler. – AudioDroid Feb 25 '11 at 15:46
  • 1
    The second `const` is superfluous **in the context of return types**. That is, the return types `const T*` and `const T* const` are completely identical, just as the return types `int` and `const int` are completely identical. – fredoverflow Feb 25 '11 at 15:54
  • @FredOverflow: I must be understanding something completely wrong. If I put the line "node = 0;" in my code. Then if I use the second const or not, let's my compiler either compile or it gives me an error. How can it be superfluous then? And I believe this to be the case, because T* is a pointer, and the second const tells my compiler, that I don't want this pointer to be changed. – AudioDroid Feb 25 '11 at 16:06
  • I'm **not** talking about the local variable `node`. Of course it makes a difference if a variable is `const` or not. I'm talking about the **return type** of the function `GetNodeReference`. You can change that to `const Node*` and it won't make any difference to the client. That is, `GetNodeReference(5) = 0` won't compile, no matter if the return type is `const Node*` or `const Node* const`. Again, I am only talking about the **return type** of the function, **not** the type of the local variable `node`. Do you understand now? – fredoverflow Feb 25 '11 at 16:12
  • @FredOverflow: Wait...okay I understand it now. Thanks for pointing that out. And sorry that it took me so long. :-/ :-) – AudioDroid Feb 25 '11 at 16:14
0

I suggest:

class LinkedList {

private:

struct Node
{
    int StoredValue;
    // ...
};

Node NodeReference;

const Node& GetNodeReference(std::size_t Index) const
{
    return NodeReference;
}

public:

int Get(std::size_t Index) const
{
    const Node node = GetNodeReference(Index);
    return node.StoredValue;
}

};

AudioDroid
  • 2,292
  • 2
  • 20
  • 31