would you consider this function const?

Discussion in 'C++' started by john, Dec 14, 2011.

  1. john

    john Guest

    Are the following functions an abuse of good usage of 'const'? I think
    func3 is an abuse, but I'm not sure if func1 or func2, in isolation,
    would be considered poor usage since they do not actually modify an
    instance of A themselves (although they do leave the door open). Thanks.

    class B;

    class A
    {

    B* m_b;

    public:

    void func1(A* &a) const
    {
    a = const_cast<A*>(this);
    }

    B* func2() const { return m_b; }

    // because we could do
    void func3() const
    {
    // now we can change m_b;
    B* b = func2();

    // now we can change this;
    A* a;
    func1(a);
    }

    };
    john, Dec 14, 2011
    #1
    1. Advertising

  2. On 12/14/2011 10:17 AM, john wrote:
    > Are the following functions an abuse of good usage of 'const'? I think
    > func3 is an abuse, but I'm not sure if func1 or func2, in isolation,
    > would be considered poor usage since they do not actually modify an
    > instance of A themselves (although they do leave the door open). Thanks.
    >
    > class B;
    >
    > class A
    > {
    >
    > B* m_b;
    >
    > public:
    >
    > void func1(A* &a) const
    > {
    > a = const_cast<A*>(this);
    > }
    >
    > B* func2() const { return m_b; }
    >
    > // because we could do
    > void func3() const
    > {
    > // now we can change m_b;
    > B* b = func2();
    >
    > // now we can change this;
    > A* a;
    > func1(a);
    > }
    >
    > };


    IME 'const_cast' is good only for one thing: to add const where one
    needs an overload resolution to go a particular way. Any other use of
    'const_cast' is an instance of abuse. That's just IME, of course.

    As with 'func2', there is nothing inherently wrong. The pointer is
    const, but the object to which the pointer points isn't. If the object
    to which m_b points is *owned* by 'this', then I think a slight change
    in design might accomplish the intended protection:

    const B* func2() const { return m_b; }
    B* funct2() { return m_b; }

    Otherwise, if m_b does not designate ownership, and hence does not call
    for ensuring protection, all is well.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 14, 2011
    #2
    1. Advertising

  3. john

    john Guest

    On 12/14/2011 11:19 AM, Victor Bazarov wrote:
    > On 12/14/2011 10:17 AM, john wrote:

    snipped ....
    >
    > IME 'const_cast' is good only for one thing: to add const where one
    > needs an overload resolution to go a particular way. Any other use of
    > 'const_cast' is an instance of abuse. That's just IME, of course.


    Could you give an example of the use of const_cast in this case? I do
    not quite follow what you are saying.

    >
    > As with 'func2', there is nothing inherently wrong. The pointer is
    > const, but the object to which the pointer points isn't. If the object
    > to which m_b points is *owned* by 'this', then I think a slight change
    > in design might accomplish the intended protection:
    >
    > const B* func2() const { return m_b; }
    > B* funct2() { return m_b; }
    >
    > Otherwise, if m_b does not designate ownership, and hence does not call
    > for ensuring protection, all is well.


    This makes sense. Thanks.

    John
    john, Dec 15, 2011
    #3
  4. On 12/15/2011 8:10 AM, john wrote:
    > On 12/14/2011 11:19 AM, Victor Bazarov wrote:
    >> On 12/14/2011 10:17 AM, john wrote:

    > snipped ....
    >>
    >> IME 'const_cast' is good only for one thing: to add const where one
    >> needs an overload resolution to go a particular way. Any other use of
    >> 'const_cast' is an instance of abuse. That's just IME, of course.

    >
    > Could you give an example of the use of const_cast in this case? I do
    > not quite follow what you are saying.


    This is from memory, I don't have a good working example right now...
    When you want to call a const member function for a non-const object,
    and a non-const member function also exists and overloads the const one,
    the non-const would be picked - it's a better match. That's where you
    could force the compiler into picking the one you want:

    #include <iostream>
    #include <ostream>
    struct foo {
    void bar() const { std::cout << "const bar\n"; }
    void bar() { std::cout << "regular bar\n"; }
    };

    int main() {
    // ...
    foo f;
    f.bar(); // non const is picked.
    const_cast<const foo&>(f).bar(); // const version
    }

    ...something like that, anyway. It also happens in the context of a
    non-const member that is [partially] implemented in terms of a const
    member function, for instance. This is artificial, of course:

    struct foo {
    void bar() const {
    std::cout << "const bar\n"; }
    void bar() {
    std::cout << "non-";
    const_cast<const foo*>(this)->bar(); }
    };

    HTH

    > [..]


    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 15, 2011
    #4
  5. john

    john Guest

    On 12/15/2011 8:21 AM, Victor Bazarov wrote:
    > On 12/15/2011 8:10 AM, john wrote:
    >> On 12/14/2011 11:19 AM, Victor Bazarov wrote:
    >>> On 12/14/2011 10:17 AM, john wrote:

    >> snipped ....
    >>>
    >>> IME 'const_cast' is good only for one thing: to add const where one
    >>> needs an overload resolution to go a particular way. Any other use of
    >>> 'const_cast' is an instance of abuse. That's just IME, of course.

    >>
    >> Could you give an example of the use of const_cast in this case? I do
    >> not quite follow what you are saying.

    >
    > This is from memory, I don't have a good working example right now...
    > When you want to call a const member function for a non-const object,
    > and a non-const member function also exists and overloads the const one,
    > the non-const would be picked - it's a better match. That's where you
    > could force the compiler into picking the one you want:
    >
    > #include <iostream>
    > #include <ostream>
    > struct foo {
    > void bar() const { std::cout << "const bar\n"; }
    > void bar() { std::cout << "regular bar\n"; }
    > };
    >
    > int main() {
    > // ...
    > foo f;
    > f.bar(); // non const is picked.
    > const_cast<const foo&>(f).bar(); // const version
    > }
    >
    > ..something like that, anyway. It also happens in the context of a
    > non-const member that is [partially] implemented in terms of a const
    > member function, for instance. This is artificial, of course:
    >
    > struct foo {
    > void bar() const {
    > std::cout << "const bar\n"; }
    > void bar() {
    > std::cout << "non-";
    > const_cast<const foo*>(this)->bar(); }
    > };
    >
    > HTH
    >
    >> [..]

    >
    > V


    I see. That makes sense.

    One of the issues that spurred this question (related to my func1 in the
    original post) is the following. I have a 3D mesh composed of
    Volumes/Faces/Edges/Vertices. So, I have a Volume, Face, Edge, and
    Vertex class, each derived from a base Topology class. The mesh is
    consistent, so given a particular instance of one of the classes, there
    are functions that return geometry information such as connectivity. In
    particular, each class has a virtual function (declared as pure virtual
    at the base Topology class)

    virtual void GetVolumes(std::set<Volume*> volumes);

    that returns a list of pointers to the volumes associated with the
    entity. I want to make this function 'const' since it does not change
    the element. For a Face, Edge, and Vertex, this poses no problem.
    However, for a volume instance, the function should return a pointer to
    just itself, so if GetVolumes is const, then I have

    void Volume::GetVolumes(std::set<Volume*> volumes) const
    {
    volumes.clear();
    volumes.insert(const_cast<Volume*>(this));
    }

    I do not see how to have the GetVolumes function const without the use
    of const_cast in the Volume::GetVolumes function. Is there a more
    intelligent way to get the same result?

    Thanks,
    John
    john, Dec 15, 2011
    #5
  6. On 12/15/2011 8:44 AM, john wrote:
    > On 12/15/2011 8:21 AM, Victor Bazarov wrote:
    >> On 12/15/2011 8:10 AM, john wrote:
    >>> On 12/14/2011 11:19 AM, Victor Bazarov wrote:
    >>>> On 12/14/2011 10:17 AM, john wrote:
    >>> snipped ....
    >>>>
    >>>> IME 'const_cast' is good only for one thing: to add const where one
    >>>> needs an overload resolution to go a particular way. Any other use of
    >>>> 'const_cast' is an instance of abuse. That's just IME, of course.
    >>>
    >>> Could you give an example of the use of const_cast in this case? I do
    >>> not quite follow what you are saying.

    >>
    >> This is from memory, I don't have a good working example right now...
    >> When you want to call a const member function for a non-const object,
    >> and a non-const member function also exists and overloads the const one,
    >> the non-const would be picked - it's a better match. That's where you
    >> could force the compiler into picking the one you want:
    >>
    >> #include <iostream>
    >> #include <ostream>
    >> struct foo {
    >> void bar() const { std::cout << "const bar\n"; }
    >> void bar() { std::cout << "regular bar\n"; }
    >> };
    >>
    >> int main() {
    >> // ...
    >> foo f;
    >> f.bar(); // non const is picked.
    >> const_cast<const foo&>(f).bar(); // const version
    >> }
    >>
    >> ..something like that, anyway. It also happens in the context of a
    >> non-const member that is [partially] implemented in terms of a const
    >> member function, for instance. This is artificial, of course:
    >>
    >> struct foo {
    >> void bar() const {
    >> std::cout << "const bar\n"; }
    >> void bar() {
    >> std::cout << "non-";
    >> const_cast<const foo*>(this)->bar(); }
    >> };
    >>
    >> HTH
    >>
    >>> [..]

    >>
    >> V

    >
    > I see. That makes sense.
    >
    > One of the issues that spurred this question (related to my func1 in the
    > original post) is the following. I have a 3D mesh composed of
    > Volumes/Faces/Edges/Vertices. So, I have a Volume, Face, Edge, and
    > Vertex class, each derived from a base Topology class. The mesh is
    > consistent, so given a particular instance of one of the classes, there
    > are functions that return geometry information such as connectivity. In
    > particular, each class has a virtual function (declared as pure virtual
    > at the base Topology class)
    >
    > virtual void GetVolumes(std::set<Volume*> volumes);


    That doesn't look right. Did you lose the ampersand before the argument
    name?

    >
    > that returns a list of pointers to the volumes associated with the
    > entity. I want to make this function 'const' since it does not change
    > the element. For a Face, Edge, and Vertex, this poses no problem.
    > However, for a volume instance, the function should return a pointer to
    > just itself, so if GetVolumes is const, then I have
    >
    > void Volume::GetVolumes(std::set<Volume*> volumes) const
    > {
    > volumes.clear();
    > volumes.insert(const_cast<Volume*>(this));
    > }
    >
    > I do not see how to have the GetVolumes function const without the use
    > of const_cast in the Volume::GetVolumes function. Is there a more
    > intelligent way to get the same result?


    What happens if you change the type of the argument to

    std::set<const Volume*>

    ? If it's a set of constant volumes, that's what the type should
    express. If you find it difficult to type, use a typedef *or* introduce
    some kind of 'Volume proxy' and keep that instead of the pointer to Volume.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 15, 2011
    #6
  7. john

    john Guest

    >>
    >> One of the issues that spurred this question (related to my func1 in the
    >> original post) is the following. I have a 3D mesh composed of
    >> Volumes/Faces/Edges/Vertices. So, I have a Volume, Face, Edge, and
    >> Vertex class, each derived from a base Topology class. The mesh is
    >> consistent, so given a particular instance of one of the classes, there
    >> are functions that return geometry information such as connectivity. In
    >> particular, each class has a virtual function (declared as pure virtual
    >> at the base Topology class)
    >>
    >> virtual void GetVolumes(std::set<Volume*> volumes);

    >
    > That doesn't look right. Did you lose the ampersand before the argument
    > name?
    >


    Yes, it should be

    virtual void GetVolumes(std::set<Volume*>& volumes);


    >>
    >> that returns a list of pointers to the volumes associated with the
    >> entity. I want to make this function 'const' since it does not change
    >> the element. For a Face, Edge, and Vertex, this poses no problem.
    >> However, for a volume instance, the function should return a pointer to
    >> just itself, so if GetVolumes is const, then I have
    >>
    >> void Volume::GetVolumes(std::set<Volume*> volumes) const
    >> {
    >> volumes.clear();
    >> volumes.insert(const_cast<Volume*>(this));
    >> }
    >>
    >> I do not see how to have the GetVolumes function const without the use
    >> of const_cast in the Volume::GetVolumes function. Is there a more
    >> intelligent way to get the same result?

    >
    > What happens if you change the type of the argument to
    >
    > std::set<const Volume*>
    >
    > ? If it's a set of constant volumes, that's what the type should
    > express. If you find it difficult to type, use a typedef *or* introduce
    > some kind of 'Volume proxy' and keep that instead of the pointer to Volume.


    I will have to look at whether I can do that or not. My guess is that,
    for the most part, I will need non-const Volume*'s. It's a huge code
    base, so a change like that could ripple to a lot of places.

    John
    john, Dec 15, 2011
    #7
  8. On 12/15/2011 9:30 AM, john wrote:
    >>>
    >>> One of the issues that spurred this question (related to my func1 in the
    >>> original post) is the following. I have a 3D mesh composed of
    >>> Volumes/Faces/Edges/Vertices. So, I have a Volume, Face, Edge, and
    >>> Vertex class, each derived from a base Topology class. The mesh is
    >>> consistent, so given a particular instance of one of the classes, there
    >>> are functions that return geometry information such as connectivity. In
    >>> particular, each class has a virtual function (declared as pure virtual
    >>> at the base Topology class)
    >>>
    >>> virtual void GetVolumes(std::set<Volume*> volumes);

    >>
    >> That doesn't look right. Did you lose the ampersand before the argument
    >> name?
    >>

    >
    > Yes, it should be
    >
    > virtual void GetVolumes(std::set<Volume*>& volumes);
    >
    >
    >>>
    >>> that returns a list of pointers to the volumes associated with the
    >>> entity. I want to make this function 'const' since it does not change
    >>> the element. For a Face, Edge, and Vertex, this poses no problem.
    >>> However, for a volume instance, the function should return a pointer to
    >>> just itself, so if GetVolumes is const, then I have
    >>>
    >>> void Volume::GetVolumes(std::set<Volume*> volumes) const
    >>> {
    >>> volumes.clear();
    >>> volumes.insert(const_cast<Volume*>(this));
    >>> }
    >>>
    >>> I do not see how to have the GetVolumes function const without the use
    >>> of const_cast in the Volume::GetVolumes function. Is there a more
    >>> intelligent way to get the same result?

    >>
    >> What happens if you change the type of the argument to
    >>
    >> std::set<const Volume*>
    >>
    >> ? If it's a set of constant volumes, that's what the type should
    >> express. If you find it difficult to type, use a typedef *or* introduce
    >> some kind of 'Volume proxy' and keep that instead of the pointer to
    >> Volume.

    >
    > I will have to look at whether I can do that or not. My guess is that,
    > for the most part, I will need non-const Volume*'s. It's a huge code
    > base, so a change like that could ripple to a lot of places.


    Well, depending on the domain and the purpose for which the pointers are
    collected, it might be OK to const_cast them like that. After all, it
    is unlikely that any object of your Volume class is ever created in a
    constant memory, so the 'const' here is just a pretense. Keep in mind,
    however, that const_cast can be abused. And don't abuse it. If you can
    rework your design in such a way that wouldn't call for the use of
    'const_cast' to cast *away* any constness, it could lead to a more
    robust system, methinks.

    I wonder (not that you need to explain, just wonder along with me), how
    is Volume and, say, Edge derive from the same class. What is common
    about them (aside from 'GetVolumes')? Also, can you have nested
    Volumes? You know, an egg inside a duck inside a hare hidden in a
    trunk... Seems like if your answer is 'no', then a Volume cannot derive
    from Topology since you essentially make Topology aware of a type that
    will be derived from it, which is usually a no-no...

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 15, 2011
    #8
  9. john

    john Guest

    >>> What happens if you change the type of the argument to
    >>>
    >>> std::set<const Volume*>
    >>>
    >>> ? If it's a set of constant volumes, that's what the type should
    >>> express. If you find it difficult to type, use a typedef *or* introduce
    >>> some kind of 'Volume proxy' and keep that instead of the pointer to
    >>> Volume.

    >>
    >> I will have to look at whether I can do that or not. My guess is that,
    >> for the most part, I will need non-const Volume*'s. It's a huge code
    >> base, so a change like that could ripple to a lot of places.

    >
    > Well, depending on the domain and the purpose for which the pointers are
    > collected, it might be OK to const_cast them like that. After all, it is
    > unlikely that any object of your Volume class is ever created in a
    > constant memory, so the 'const' here is just a pretense. Keep in mind,
    > however, that const_cast can be abused. And don't abuse it. If you can
    > rework your design in such a way that wouldn't call for the use of
    > 'const_cast' to cast *away* any constness, it could lead to a more
    > robust system, methinks.
    >
    > I wonder (not that you need to explain, just wonder along with me), how
    > is Volume and, say, Edge derive from the same class. What is common
    > about them (aside from 'GetVolumes')? Also, can you have nested Volumes?
    > You know, an egg inside a duck inside a hare hidden in a trunk... Seems
    > like if your answer is 'no', then a Volume cannot derive from Topology
    > since you essentially make Topology aware of a type that will be derived
    > from it, which is usually a no-no...



    Well, a lot of functionality is common to all element types. For
    example, every Topology (be it a volume, edge, face, vertex) has
    functions that can return its unique id, its center point, its bounding
    vertices, its 'type', its dimension. Every Topology stores another
    class object that can compute geometry on itself, for example position
    vector, unitary vectors, etc. So there is a lot of common functionality
    that is declared as pure virtual or virtual at the base class and some
    common data members.

    To answer you other question, nested volumes are not allowed. I do not
    understand you comment that Volume cannot derive from Topology. I do
    not see a problem with doing it.
    john, Dec 15, 2011
    #9
  10. On 12/15/2011 12:42 PM, john wrote:
    > [...] I do not
    > understand you comment that Volume cannot derive from Topology. I do not
    > see a problem with doing it.


    Nah... Forget I mentioned it. Do a const_cast. Five years from now
    expect a maintenance nightmare. If it doesn't materialize, consider
    yourself lucky. If your program doesn't live that long, all the better.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Dec 15, 2011
    #10
  11. john

    none Guest

    In article <jcai9t$ee9$>,
    Victor Bazarov <> wrote:
    >
    >IME 'const_cast' is good only for one thing: to add const where one
    >needs an overload resolution to go a particular way. Any other use of
    >'const_cast' is an instance of abuse. That's just IME, of course.


    I would tolerate const_cast inside a method when conceptually, a
    method is const (i.e. does not conceptually change the object) but due
    to implementation details, the object internal needs to be modified.

    I very rarely see a reason to use it. But for example, some peoples
    are fans of delaying initialisation using a pattern like:

    class A
    {
    public:
    A(): m_initDone(false), ... {};
    std::string foo() const
    {
    if(!m_initDone)
    {
    const_cast<A *>(this)->init();
    }
    // do the work
    }
    private:
    void init() {...};
    bool m_initDone;
    // other member variables
    }

    I am not a big fan of the above but this would be an example where
    const_cast allow an object to modify its internals without breaking
    the concept that the object has not changed.

    Unfortunately, most of the time const_cast is used to cast-away
    constness from code that is const-correct to interface with
    const-incorrect code when the correct thing to do would be fix the
    const-incorrect code :-( (Actually, most of the time I see it, C-style
    hard casts are being used which is even worse).


    Yannick
    none, Dec 19, 2011
    #11
  12. none <yatremblay@bel1lin202.> wrote:
    > I very rarely see a reason to use it. But for example, some peoples
    > are fans of delaying initialisation using a pattern like:
    >
    > class A
    > {
    > public:
    > A(): m_initDone(false), ... {};
    > std::string foo() const
    > {
    > if(!m_initDone)
    > {
    > const_cast<A *>(this)->init();
    > }
    > // do the work
    > }
    > private:
    > void init() {...};
    > bool m_initDone;
    > // other member variables
    > }
    >
    > I am not a big fan of the above but this would be an example where
    > const_cast allow an object to modify its internals without breaking
    > the concept that the object has not changed.


    IMO it's better to declare the members that are touched by the
    initialization as 'mutable'. In most cases, those are only a few large
    objects.
    This allows you to declare the init() method as const and you don't have to
    cast anything.

    Tobi
    Tobias Müller, Dec 19, 2011
    #12
  13. On Dec 19, 11:05 am, Tobias Müller <> wrote:
    > none <yatremblay@bel1lin202.> wrote:
    > > I very rarely see a reason to use it.  But for example, some peoples
    > > are fans of delaying initialisation using a pattern like:

    >
    > > class A
    > > {
    > > public:
    > >    A(): m_initDone(false), ... {};
    > >    std::string foo()  const
    > >    {
    > >       if(!m_initDone)
    > >       {
    > >          const_cast<A *>(this)->init();
    > >       }
    > >       // do the work
    > >    }
    > > private:
    > >    void init() {...};
    > >    bool m_initDone;
    > >    // other member variables
    > > }

    >
    > > I am not a big fan of the above but this would be an example where
    > > const_cast allow an object to modify its internals without breaking
    > > the concept that the object has not changed.

    >
    > IMO it's better to declare the members that are touched by the
    > initialization as 'mutable'. In most cases, those are only a few large
    > objects.
    > This allows you to declare the init() method as const and you don't have to
    > cast anything.


    Why would you ever declare an init method as const? An init method
    must be the poster child of a method which modifies the object.

    Upon 2 seconds of reflection, is this because you're writing code
    without exceptions and more or less without constructors? I guess in
    that light your comment isn't incredibly silly - a const object still
    needs to be initialized, and in that case one isn't using constructors
    because one isn't using exceptions, and one has decided to go with the
    init method approach as opposed to the zombie object flag.
    Joshua Maurice, Dec 19, 2011
    #13
  14. Joshua Maurice <> wrote:
    > Why would you ever declare an init method as const? An init method
    > must be the poster child of a method which modifies the object.
    >
    > Upon 2 seconds of reflection, is this because you're writing code
    > without exceptions and more or less without constructors? I guess in
    > that light your comment isn't incredibly silly - a const object still
    > needs to be initialized, and in that case one isn't using constructors
    > because one isn't using exceptions, and one has decided to go with the
    > init method approach as opposed to the zombie object flag.


    If you didn't read the post I was referring to: we're talking about /lazy/
    initialization, i.e. initialization on first use. The first use could be a
    const method and if you don't want to const_cast, init must be const which
    is only possible if the initialized members are declared mutable.

    Imagine for example an XML DOM tree that is only parsed on demand, and only
    those parts that are actually used.

    Tobi
    Tobias Müller, Dec 20, 2011
    #14
  15. On Dec 19, 10:28 pm, Tobias Müller <> wrote:
    > Joshua Maurice <> wrote:
    > > Why would you ever declare an init method as const? An init method
    > > must be the poster child of a method which modifies the object.

    >
    > > Upon 2 seconds of reflection, is this because you're writing code
    > > without exceptions and more or less without constructors? I guess in
    > > that light your comment isn't incredibly silly - a const object still
    > > needs to be initialized, and in that case one isn't using constructors
    > > because one isn't using exceptions, and one has decided to go with the
    > > init method approach as opposed to the zombie object flag.

    >
    > If you didn't read the post I was referring to: we're talking about /lazy/
    > initialization, i.e. initialization on first use. The first use could be a
    > const method and if you don't want to const_cast, init must be const which
    > is only possible if the initialized members are declared mutable.
    >
    > Imagine for example an XML DOM tree that is only parsed on demand, and only
    > those parts that are actually used.


    I see. My apologies. The init method isn't a public exposed function,
    but merely a internal implementation detail.

    (The alternative is of course to const_cast before calling init, and
    having init be non-const. I don't realy think there's much of a
    difference between the two approaches, so nevermind.)
    Joshua Maurice, Dec 20, 2011
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    254
  2. Replies:
    8
    Views:
    294
    Clever Monkey
    Jul 10, 2006
  3. Replies:
    3
    Views:
    325
    Jim Langston
    Jul 12, 2006
  4. Javier
    Replies:
    2
    Views:
    542
    James Kanze
    Sep 4, 2007
  5. Tech07

    Maybe you should consider this

    Tech07, Sep 27, 2009, in forum: C Programming
    Replies:
    20
    Views:
    578
    Nick Keighley
    Oct 6, 2009
Loading...

Share This Page