简体   繁体   中英

const-correctness in void methods and lambda 'trick'

I have a method that accepts a reference of an object as const, this method doesn't change anything of the method and the const indicates that, the thing is that this method also calls other method that is within the class and is void, doesn't accept any argument and is also virtual, meaning that the class that extends the base class can override the method BUT it needs to be const as well. Eg:

#include <iostream>

class Boz
{
public:
    virtual void introduce() const = 0;
};

class Foo
{
public:
    virtual void callable() const
    {
        // ...
    }

    void caller(const Boz& object) const
    {
        callable();
        object.introduce();
    }
};

class Bar : public Boz
{
public:
    void introduce() const
    {
        std::cout << "Hi." << std::endl;
    }
};

class Biz : public Foo
{
public:
    void callable() const
    {
        std::cout << "I'm being called before the introduce." << std::endl;
    }
};

int main(void)
{
    Biz biz;
    biz.caller(Bar());

    return 0;
}

The output would be:

I'm being called before the introduce.
Hi.

As you can see callable must to be const in order to be called. If I change and do this:

class Biz : public Foo
{
public:
    void callable()
    {
        std::cout << "I'm being called before the introduce." << std::endl;
    }
};

It will compile not errors are thrown but the callable method won't be called, but the virtual one as it's defined as const. It's quite obvious.

The trickiest part here:

class Foo
{
public:
    virtual void callable()
    {
        // ...
    }

    void caller(const Boz& object) const
    {
        auto trick = [&] () { callable(); };

        trick();

        object.introduce();
    }
};

class Biz : public Foo
{
public:
    void callable()
    {
        std::cout << "I'm being called before the introduce." << std::endl;
    }
};

It works and the callable method is called. No errors like passing 'const ...' as 'this' argument .

What I'm trying to do is to call callable without the need of being const and the reason is simple: The method doesn't change anything, he don't have access to the object that is begin passed as argument on caller method then we assume that he doesn't have the need to be const but the compiler throws an error even that way. The real problem is that callable is virtual and classes can extend the base class, implement their own callable and try to call other methods but can't if it's not const as well.

What I want is pretty much that, is to know how can I call the virtual method without the need of being const (the reason is pretty much that, I'm kind forcing the users that extends the class and override the callable method to only call const methods and this is not what I want) and of course understand what happens with the lambda and why it works.

That code with the lambda definitely shouldn't compile, it's simply a GCC bug (reported as PR 60463 and PR 60755 ) which is now fixed in the svn trunk, by http://gcc.gnu.org/r210292

If you really need to call a non-const member function from a const one you need to cast away the constness:

const_cast<Foo*>(this)->callable();

But this is quite risky, for at least two reasons

  1. if the object was declared const then it is undefined behaviour eg const Foo f; f.caller(boz); const Foo f; f.caller(boz); is undefined behaviour.

  2. you're calling a virtual function, you don't necessarily know that the derived class' override definitely doesn't modify anything. The whole point of virtual functions is that a derived class can do something different and the base class doesn't know the details.

I would change your design so that the virtual function you want to call is const , or the caller function is non-const. Anything else is dangerous.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM