Calling const/non-const overrides

Hi, are there other ways of calling a const/non-const override? I want to defined some functions in terms of others, particularly accessors which might or might not require constness- in order to not copy & paste code. This is my current solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct dumbArray {
    dumbArray(unsigned int size):
        m_array(new int[size]){
    }
    ~dumbArray(){
        delete m_array;
    }

    const int& operator[](unsigned int _index) const {
        std::cout << "dumbArray const operator[] called" << std::endl;
        return (const_cast<dumbArray&>(*this))[_index];
    }

    int& operator[](unsigned int _index){
        std::cout << "dumbArray non-const operator[] called" << std::endl;
        return m_array[_index];
    }

    int* m_array;
};



Edit: Just cleaned it a little
Last edited on
Your code is backwards - the non-const version should call the const version. Right now, you have the const version calling the non-const version, and you're using a very dangerous const-cast when you don't know for a fact that you're allowed to.

Generally though you don't need a non-const version if you need a const version (though in this case of course you do need both).
Last edited on
Here, you don't need a cast at all; there is an implicit conversion from T& to const T&

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct dumbArray {

    // ...

    const int& operator[](unsigned int _index) const {
        std::cout << "dumbArray const operator[] called" << std::endl;
        return m_array[_index]; // implicit conversion from int& => const int&
    }

    int& operator[](unsigned int _index){
        std::cout << "dumbArray non-const operator[] called" << std::endl;
        return m_array[_index];
    }

    int* m_array;
};



> Generally though you don't need a non-const version if you need a const version

"Generally" above means "when we want the result type and the implementation to be identical for both const and non-const objects".
Ugh, my mistake. I had done something along the lines of what you two have suggested, but I got an infinite loop, and then changed my code to what I have posted in my first post, which worked.

Thanks for the pointer- I find these sort of things so easy to overlook.

Edit:

JLBorges- that isn't what I want to do. What I want is for either the const or non-const overload to call the other. What you posted doesn't do that.

LB: Can you post an example of how to do what you described? I agree that I'd rather the non-const call the const overload, but I'm not sure on the exact syntax of how to do that.
Last edited on
There is a difference infinite recursion and an infinite loop - JLBorges' solution results in neither.
I know, but it doesnt do what I want it to. The idea is to have one of the overloads call the other, so that I dont need to duplicate the code. Obviously in this example its rather pointless, but this is just an example. What he has is just a duplicated function, one with, and one without constness. What I want is to have the const version return the non-const version.

1
2
3
4
5
6
7
8
const int& dumbArray::operator[](unsigned int _index) const {
    std::cout << "dumbArray const operator[] called" << std::endl;
    return (*this)[_index]; // I want this to call the non-const overload, seen below. 
}
int& dumbArray::operator[](unsigned int _index){
    std::cout << "dumbArray non-const operator[] called" << std::endl;
    return m_array[_index];
}


Without a const-cast, for the reasons you said in your first post LB
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>

struct A
{
    void foo()
    {
        std::cout << "A::foo() => " ;
        const_cast< const A& >(*this).foo() ;
    }

    void foo() const
    {
        std::cout << "A::foo() const => " ;
        const_cast< const volatile A& >(*this).foo() ;
    }

    void foo() volatile
    {
        std::cout << "A::foo() volatile => " ;
        const_cast< const volatile A& >(*this).foo() ;
    }

    void foo() const volatile { std::cout << "A::foo() const volatile\n" ; }
};

int main()
{
    A a ;
    a.foo() ; // A::foo() => A::foo() const => A::foo() const volatile

    const A& ca = a ;
    ca.foo() ; // A::foo() const => A::foo() const volatile

    volatile A& va = a ;
    va.foo() ; // A::foo() volatile => A::foo() const volatile

    const volatile A& cva = a ;
    cva.foo() ; // A::foo() const volatile
}


Without casts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>

struct A
{
    void foo()
    {
        std::cout << "A::foo() => " ;
        const A& This = *this ;
        This.foo() ;
    }

    void foo() const
    {
        std::cout << "A::foo() const => " ;
        const volatile A& This = *this ;
        This.foo() ;
    }

    void foo() volatile
    {
        std::cout << "A::foo() volatile => " ;
        const volatile A& This = *this ;
        This.foo() ;
    }

    void foo() const volatile { std::cout << "A::foo() const volatile\n" ; }
};

int main()
{
    A a ;
    a.foo() ; // A::foo() => A::foo() const => A::foo() const volatile

    const A& ca = a ;
    ca.foo() ; // A::foo() const => A::foo() const volatile

    volatile A& va = a ;
    va.foo() ; // A::foo() volatile => A::foo() const volatile

    const volatile A& cva = a ;
    cva.foo() ; // A::foo() const volatile
}
Last edited on
I think there is some misunderstanding here - jaded7 wants "to have the const version return the non-const version", but this doesn't make sense. Can we have some clarification?
> "to have the const version return the non-const version", but this doesn't make sense.

It can engender undefined behaviour.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

struct A
{
    constexpr A( int v ) : i(v) {}
    void foo() { std::cout << ++i << '\n' ; }
    void foo() const { const_cast<A&>(*this).foo() ; }
    int i ;
};

int main()
{
    static const A a(23) ;
    a.foo() ;
}

http://coliru.stacked-crooked.com/a/9336026606af6b09
I know, which is why I said it doesn't make sense.
For operator[] with usual semantics, non-const overload doesn't modify the state of *this, it only provides a reference that others may use to modify it, so const_cast is actually considered to be justified (e.g. by Scott Meyers) in this case if code reuse is needed.. but as JLBorges pointed out, duplicate code is much simpler in this case.

(but returning const int& seems silly to me: what kind of member functions you're expecting the caller to invoke in the returned lvalue? Just return int)
Last edited on
Topic archived. No new replies allowed.