Constant constructor

Is it possible in C ++ to achieve something like a constructor that is allowed to create objects const

?

I'm going to make the decorator class an interface with methods const

and non const

. Initializing a decorator from a base object const

should only be able to create const constructors, but initializing from a non-const should yield a fully functional decorator.

struct A
{
    virtual void foo();         // requires non-const A
    virtual void bar() const;   // const method
};

class decorator : public A
{
private:
    std::shared_ptr<A> p_impl;
public:
    virtual void foo()          { p_impl->foo(); }
    virtual void bar() const    { p_impl->bar(); }

    // regular constructor
    decorator(std::shared_ptr<A> const & p)             : p_impl(p) {} 

    // hypothetical constructor that makes a const
    decorator(std::shared_ptr<A const> const & p) const : p_impl(p) {} 
};

void F(std::shared_ptr<A> const       & regular_a
     , std::shared_ptr<A const> const & const_a   )
{
    decorator regular_decorator(regular_a);
    regular_decorator.foo(); // all good
    regular_decorator.bar(); // all good

    decorator bad_decorator(const_a);   // compiler error
    // trying to use a const constructor to init a non-const object

    const decorator const_decorator(const_a); // all good
    const_decorator.foo(); // compiler error, foo is not const
    const_decorator.bar(); // all good

    // I have a lot of these in code that is beyond my control
    decorator bad_practice(const_cast<decorator&>(const_decorator));

    bad_practice.foo(); // all good
}

      

How can I achieve a similar effect?

+3


source to share


1 answer


I managed to get this working without having a constructor that returns an object const

, but a static function (a-la named constructor) that returns shared_ptr<const decorator>

. This "encodes" a constant in the type and disallows non-const calls:

struct A
{
    virtual void foo();         // requires non-const A
    virtual void bar() const;   // const method
};

class decorator : public A
{
private:
    std::shared_ptr<A> p_impl;
public:
    virtual void foo()          { p_impl->foo(); }
    virtual void bar() const    { p_impl->bar(); }

    // regular constructor
    decorator(std::shared_ptr<A> const & p)             : p_impl(p) {} 

    static std::shared_ptr<decorator const> constDecorator(std::shared_ptr<A const> const & p) { return std::make_shared<decorator>(std::const_pointer_cast<A>(p)); } 
};

void F(std::shared_ptr<A> const       & regular_a
     , std::shared_ptr<A const> const & const_a   )
{
    decorator regular_decorator(regular_a);
    regular_decorator.foo(); // all good
    regular_decorator.bar(); // all good

    decorator bad_decorator(const_a);   // compiler error
    // trying to use a const constructor to init a non-const object

    std::shared_ptr<const decorator> const_decorator = decorator::constDecorator(const_a); // all good
    const_decorator->foo(); // compiler error, foo is not const
    const_decorator->bar(); // all good

    // I have a lot of these in code that is beyond my control
    decorator bad_practice(const_cast<decorator&>(*const_decorator));
    bad_practice.foo(); // all good
}

      

You can, of course, use shared_ptr

for non-const decorators by declaring another static function and thus get similar usage patterns for both const and non-const.



Note that this will require removing the copy constructor and operator=

for decorator

, as they will lose the constant. However, a similar problem exists in your version with the hypothetical const constructor.

Another approach I tried to do was to make decorator

a template class and have two different types: decorator<A>

and decorator<const A>

, hoping that the compiler won't instantiate decorator<const A>::foo()

if not in use, but it keeps an instance of it even if not in use.

+1


source







All Articles