Overloading funtions with const qualifier

Discussion in 'C++' started by matthias_k, Jan 23, 2005.

  1. matthias_k

    matthias_k Guest

    Hi,

    I've never thought about this before, but since you are able to overload
    a function only by changing the const-ness of the formal parameters,
    some annoying side effects will arise.

    For example, if you only have this code ...

    void foo( const int n ) {}

    int a;
    foo( a );

    .... the compiler will not complain about the non-constness of a.
    This is not surprising, I always treated the const qualifier for a
    parameter as some kind of contract between me an d the client that the
    actual parameter will never be modified.
    However, if you overload foo, like this ...

    void foo( int n ) {} // 1
    void foo( const int n ) {} // 2

    int a;
    foo( a ); // 1

    int const b;
    foor( b ); // 2

    .... the compiler suddenly cares about the const-ness of the argument.
    So, if you initially have this code:

    void foo( const int n ) {}

    int a;
    foo( a );

    and suddenly some coder comes along and overloads foo in the way
    mentioned above, a completely different function is called!

    Maybe it's not such a good idea afterall to make parameters const in
    order to imply non-modifying behavior??

    Eager to hear your opinions,
    - Matthias
    matthias_k, Jan 23, 2005
    #1
    1. Advertising

  2. "matthias_k" <> wrote in message
    news:ct0s4h$je9$01$-online.com...

    > However, if you overload foo, like this ...
    >
    > void foo( int n ) {} // 1
    > void foo( const int n ) {} // 2


    This is not overloading; it is two definitions of the same function. So the
    compiler should reject it as a multiple definition.

    C++ always discards top-level const in parameter types.

    Note that in

    void foo(int* n);
    void foo(const int* n);

    the const here is not a top-level const and therefore is not discarded. But
    in

    void foo(int* n);
    void foo(int* const n);

    the const *is* a top-level const, so again this last example is two
    declarations of the same function.
    Andrew Koenig, Jan 23, 2005
    #2
    1. Advertising

  3. matthias_k

    dtmoore Guest

    matthias_k wrote:
    > Hi,
    >
    > I've never thought about this before, but since you are able to

    overload
    > a function only by changing the const-ness of the formal parameters,
    > some annoying side effects will arise.
    >
    > For example, if you only have this code ...
    >
    > void foo( const int n ) {}
    >
    > int a;
    > foo( a );
    >
    > ... the compiler will not complain about the non-constness of a.


    Of course not ... foo takes its arguments by value, so it copies a to
    get the value of n. All the compiler will care about is if you try to
    change the value of n within the body of foo.

    > This is not surprising, I always treated the const qualifier for a
    > parameter as some kind of contract between me an d the client that

    the
    > actual parameter will never be modified.


    You should "treat the const qualifier" as a fundamental element of C++
    that permeates the entire development process. Deciding which
    functions in an interface to make const is of central importance to
    designing your software. Assuming of course that you observe proper
    const-correctness throughout your software (see below).

    > However, if you overload foo, like this ...
    >
    > void foo( int n ) {} // 1
    > void foo( const int n ) {} // 2
    >
    > int a;
    > foo( a ); // 1
    >
    > int const b;
    > foor( b ); // 2
    >
    > ... the compiler suddenly cares about the const-ness of the argument.


    Nope, wrong. Statements 1 and 2 above represent identical declarations
    as far as calling foo is concerned. Since both have definitions, a
    Standard Compliant compiler should reject the code.

    > So, if you initially have this code:
    >
    > void foo( const int n ) {}
    >
    > int a;
    > foo( a );
    >
    > and suddenly some coder comes along and overloads foo in the way
    > mentioned above, a completely different function is called!


    No (see above).

    > Maybe it's not such a good idea afterall to make parameters const in
    > order to imply non-modifying behavior??
    >
    > Eager to hear your opinions,
    > - Matthias



    Proper understanding of how to use const is essential to correct
    programming in C++, and it seems like maybe you have some pretty deep
    misunderstandings about it. I suggest reviewing a good book, such as
    Herb Sutter's "Exceptional C++" or Scott Meyer's "Effective C++". At
    least you should look at: http://www.gotw.ca/gotw/006.htm
    HTH,

    Dave Moore
    dtmoore, Jan 23, 2005
    #3
  4. dtmoore wrote:

    > Proper understanding of how to use const is essential to correct
    > programming in C++, and it seems like maybe you have some pretty deep
    > misunderstandings about it. I suggest reviewing a good book, such as
    > Herb Sutter's "Exceptional C++" or Scott Meyer's "Effective C++". At
    > least you should look at: http://www.gotw.ca/gotw/006.htm
    > HTH,


    I can't argue with your suggested reading list, but I think your diagnosis may
    be overly pessimistic. :) It looks like the OP was confused because he didn't
    know the rules for parameter type decay from 8.3.5/3, which are important but a
    bit obscure.

    Jonathan
    Jonathan Turkanis, Jan 24, 2005
    #4
  5. Andrew Koenig wrote:

    > matthias_k wrote:
    >
    >>However, if you overload foo, like this ...
    >>
    >> void foo( int n) { } // 1
    >> void foo(const int n) { } // 2

    >
    > This is not overloading; it is two definitions of the same function.
    > So the compiler should reject it as a multiple definition.
    >
    > C++ always discards top-level const in parameter types.
    >
    > Note that in
    >
    > void foo(int* p);
    > void foo(const int* p);
    >
    > the const here is not a top-level const and therefore is not discarded.


    > cat foo.cc

    #include <iostream>

    void foo( int* p) { // 1
    std::clog << "foo(int*)" << std::endl;
    }

    void foo(const int* p) { // 2
    std::clog << "foo(const int*)" << std::endl;
    }

    int main() {
    const
    int m = 0;
    int n = 0;
    foo(&m);
    foo(&n);
    return 0;
    }

    > g++ -Wall -ansi -pedantic -o foo foo.cc
    > ./foo

    foo(const int*)
    foo(int*)

    This seems to suggest that,
    if you define foo(const int*), you should also define

    inline
    void foo( int* p) {
    foo((const int*)p);
    }

    to prevent the "client" from overloading foo in this way.

    > But in
    >
    > void foo(int* n);
    > void foo(int* const n);
    >
    > the const *is* a top-level const so, again,
    > this last example is two declarations of the same function.
    E. Robert Tisdale, Jan 24, 2005
    #5
  6. matthias_k

    matthias_k Guest

    Andrew Koenig wrote:
    > "matthias_k" <> wrote in message
    > news:ct0s4h$je9$01$-online.com...
    >
    >
    >>However, if you overload foo, like this ...
    >>
    >> void foo( int n ) {} // 1
    >> void foo( const int n ) {} // 2

    >
    >
    > This is not overloading; it is two definitions of the same function. So the
    > compiler should reject it as a multiple definition.
    >


    Hm, I didn't try to compile this example (since I pulled it out of my
    hat), but I had a very similar problem which lead me to this question
    and which *did* compile. However, in that last case the two functions
    differed in their return types:

    inline std::string str_to_lower ( const std::string& source )
    {
    std::string target;
    std::transform( source.begin(), source.end(),
    std::back_inserter(target), to_lower );
    return target;
    }

    inline void str_to_lower ( std::string& source )
    {
    std::transform( source.begin(), source.end(), source.begin(),
    to_lower );
    }

    Those two functions convert an std::string to lower case, but the first
    is non-modifying and the second is modifying. The compiler didn't complain.

    Regards,
    Matthias
    matthias_k, Jan 24, 2005
    #6
  7. matthias_k

    matthias_k Guest

    Andrew Koenig wrote:
    > "matthias_k" <> wrote in message
    > news:ct0s4h$je9$01$-online.com...


    > C++ always discards top-level const in parameter types.
    >


    What does "top-level const" mean?

    Regards,
    Matthias
    matthias_k, Jan 24, 2005
    #7
  8. matthias_k

    matthias_k Guest

    dtmoore wrote:
    > matthias_k wrote:
    >>and suddenly some coder comes along and overloads foo in the way
    >>mentioned above, a completely different function is called!

    >
    >
    > No (see above).
    >


    So what would happen if I would call foo on references?
    void foo( int& n );
    void foo( const int& n );

    >
    >>Maybe it's not such a good idea afterall to make parameters const in
    >>order to imply non-modifying behavior??
    >>
    >>Eager to hear your opinions,
    >>- Matthias

    >
    >
    >
    > Proper understanding of how to use const is essential to correct
    > programming in C++, and it seems like maybe you have some pretty deep
    > misunderstandings about it. I suggest reviewing a good book, such as
    > Herb Sutter's "Exceptional C++" or Scott Meyer's "Effective C++". At
    > least you should look at: http://www.gotw.ca/gotw/006.htm
    > HTH,


    I have read both Effective books by Scott Meyers and I recently bought
    the third one on STL. I don't see though what exactly I am
    misunderstanding about the keyword const. Maybe you could just tell me?

    >
    > Dave Moore
    >


    Thanks,
    Matthias
    matthias_k, Jan 24, 2005
    #8
  9. matthias_k

    matthias_k Guest

    Jonathan Turkanis wrote:
    > dtmoore wrote:
    >
    >
    >>Proper understanding of how to use const is essential to correct
    >>programming in C++, and it seems like maybe you have some pretty deep
    >>misunderstandings about it. I suggest reviewing a good book, such as
    >>Herb Sutter's "Exceptional C++" or Scott Meyer's "Effective C++". At
    >>least you should look at: http://www.gotw.ca/gotw/006.htm
    >>HTH,

    >
    >
    > I can't argue with your suggested reading list, but I think your diagnosis may
    > be overly pessimistic. :) It looks like the OP was confused because he didn't
    > know the rules for parameter type decay from 8.3.5/3, which are important but a
    > bit obscure.
    >
    > Jonathan
    >
    >


    Hi Jonathan,

    unfortunately I don't have a copy of the standard, what is this passage
    about exactly?

    Thanks,
    Matthias
    matthias_k, Jan 24, 2005
    #9
  10. matthias_k

    Dave Moore Guest

    "matthias_k" <> wrote in message
    news:ct25uf$aog$02$-online.com...
    > dtmoore wrote:
    > > matthias_k wrote:
    > >>and suddenly some coder comes along and overloads foo in the way
    > >>mentioned above, a completely different function is called!

    > >
    > >
    > > No (see above).
    > >

    >
    > So what would happen if I would call foo on references?
    > void foo( int& n );
    > void foo( const int& n );


    Well, obviously that can make a big difference, because now you have
    redefined foo for pass by reference, and so the effective scope of the
    parameter is different. That is, any changes you make through the reference
    parameter will now be reflected outside the scope of foo. Also, your
    compiler will not complain, because you (or someone else) have now done a
    legal overload of foo.

    This is an altogether different situation from the example in your different
    post. While it is completely normal to have const and non-const versions of
    member functions (which implicitly take const or non-const references to the
    object they are called for), I cannot immediately see a legitimate reason to
    overload non-member functions in this way (although that doesn't mean there
    isn't one). If you are worried about "accidental" overloading, the best way
    to handle that is probably to put the function inside a namespace. You
    could also define your own non-const version of foo simply as:
    void foo(int &n) {
    foo(const_cast<const int&>(n));
    }
    Note that this is a "good" use of const_cast, since you are casting *to* the
    const type. This will then generate a compile-time error if someone tries
    to overload it.

    If you are worried about "malicious" overloading, then I have no useful
    suggestions ... dedicated hackers will probably be able to defeat anything I
    can devise.

    > >
    > > Proper understanding of how to use const is essential to correct
    > > programming in C++, and it seems like maybe you have some pretty deep
    > > misunderstandings about it. I suggest reviewing a good book, such as
    > > Herb Sutter's "Exceptional C++" or Scott Meyer's "Effective C++". At
    > > least you should look at: http://www.gotw.ca/gotw/006.htm
    > > HTH,

    >
    > I have read both Effective books by Scott Meyers and I recently bought
    > the third one on STL. I don't see though what exactly I am
    > misunderstanding about the keyword const. Maybe you could just tell me?
    >


    Maybe you have no misunderstandings concerning const... I may have read too
    much between the lines from your first post. If so, then please accept my
    apologies.

    HTH,

    Dave Moore
    Dave Moore, Jan 24, 2005
    #10
  11. matthias_k

    matthias_k Guest

    Dave Moore wrote:
    > This is an altogether different situation from the example in your different
    > post. While it is completely normal to have const and non-const versions of
    > member functions (which implicitly take const or non-const references to the
    > object they are called for), I cannot immediately see a legitimate reason to
    > overload non-member functions in this way (although that doesn't mean there
    > isn't one).

    Yes, my example wasn't too good, I should have posted the original code
    from the beginning. Please see my other answer where I also posted the
    code of the two functions from which my problem arose.
    They are basically two versions of the same function, one which is
    modifying, the other being non-modifying.

    If you are worried about "accidental" overloading, the best way
    > to handle that is probably to put the function inside a namespace. You
    > could also define your own non-const version of foo simply as:
    > void foo(int &n) {
    > foo(const_cast<const int&>(n));
    > }
    > Note that this is a "good" use of const_cast, since you are casting *to* the
    > const type. This will then generate a compile-time error if someone tries
    > to overload it.


    The problem is that, in my case, I want to have both versions of the
    function (see other post). They are both in the same namespace. If I now
    want to explicitly call the non-modifying version, I have to say:

    const std::string src( "WHATEVER" );
    std::string ret = str_to_lower( src ); // call the non-modifying version

    If I remove the const qualifier from src, this piece of code won't
    compile. It would however, if I had only the non-modifying version of
    the function. That's the dilemma I was talking about. In my case this
    isn't too much of a problem because I have written both functions and I
    know how to use them, but this may not always be the case.

    Regards,
    Matthias
    matthias_k, Jan 24, 2005
    #11
  12. matthias_k

    Dave Moore Guest

    "matthias_k" <> wrote in message
    news:ct2un3$f3v$01$-online.com...
    > Dave Moore wrote:
    > > This is an altogether different situation from the example in your

    different
    > > post. While it is completely normal to have const and non-const

    versions of
    > > member functions (which implicitly take const or non-const references to

    the
    > > object they are called for), I cannot immediately see a legitimate

    reason to
    > > overload non-member functions in this way (although that doesn't mean

    there
    > > isn't one).

    > Yes, my example wasn't too good, I should have posted the original code
    > from the beginning. Please see my other answer where I also posted the
    > code of the two functions from which my problem arose.
    > They are basically two versions of the same function, one which is
    > modifying, the other being non-modifying.
    >
    > If you are worried about "accidental" overloading, the best way
    > > to handle that is probably to put the function inside a namespace. You
    > > could also define your own non-const version of foo simply as:
    > > void foo(int &n) {
    > > foo(const_cast<const int&>(n));
    > > }
    > > Note that this is a "good" use of const_cast, since you are casting *to*

    the
    > > const type. This will then generate a compile-time error if someone

    tries
    > > to overload it.

    >
    > The problem is that, in my case, I want to have both versions of the
    > function (see other post). They are both in the same namespace.


    Well, I think you should seriously re-examine your need to have both
    versions with the same name. The whole point of overloading is to clarify,
    not obfuscate. What would be lost if you were to change the name of one
    version of the function?

    > If I now
    > want to explicitly call the non-modifying version, I have to say:
    >
    > const std::string src( "WHATEVER" );
    > std::string ret = str_to_lower( src ); // call the non-modifying version


    Ok .. so here you have a workaround if you need one .. you could also use
    the const_cast technique from my earlier post. However, I agree that both
    are unwieldy? So again, why do you want two versions of the "same" function
    that behave differently?

    Dave Moore


    Dave Moore
    Dave Moore, Jan 25, 2005
    #12
  13. matthias_k

    msalters Guest

    matthias_k wrote:
    > Andrew Koenig wrote:
    > > "matthias_k" <> wrote in message
    > > news:ct0s4h$je9$01$-online.com...

    >
    > > C++ always discards top-level const in parameter types.

    >
    > What does "top-level const" mean?


    const can appear in a number of positions when naming a type,
    e.g. int const* or int * const [ or int const * const ]. For a type T,
    at most one of the const's in T applies to the sizeof(T) bytes of
    the object itself. This const, if present is called the top-most
    const. In the examples, int * const has a top-level const (it's
    a const pointer to a non-const int) while int const* doesn't
    (the pointer can be modified, but not the int's pointed to.

    In practice, it often is the right-most const, but there are
    counter-examples:

    void (*const functionptr) ( int const* arg ) = &foo;

    The second const refers to arg. The first const actually makes
    this a constant pointer.

    Regards,
    Michiel Salters
    msalters, Jan 25, 2005
    #13
  14. matthias_k

    Matthias Guest

    >
    > Well, I think you should seriously re-examine your need to have both
    > versions with the same name. The whole point of overloading is to clarify,
    > not obfuscate. What would be lost if you were to change the name of one
    > version of the function?


    That is true of course. I just thought that it may be easier for the
    client to just remember one name :)
    But in this case, it's indeed better to name them differently, maybe
    str_to_lower_const and str_to_lower or such.
    The point was just that this was a situation I had not encountered
    before, so I thought I'd better ask here how to handle this properly.
    Matthias, Jan 26, 2005
    #14
    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. Sergey Tolstov

    const qualifier and VC6.0

    Sergey Tolstov, Oct 7, 2003, in forum: C++
    Replies:
    8
    Views:
    454
    Howard
    Oct 7, 2003
  2. Mahesh Tomar

    Const Qualifier question

    Mahesh Tomar, Sep 1, 2004, in forum: C++
    Replies:
    4
    Views:
    356
    Old Wolf
    Sep 2, 2004
  3. Replies:
    1
    Views:
    283
    Bob Hairgrove
    Jan 30, 2006
  4. Javier
    Replies:
    2
    Views:
    533
    James Kanze
    Sep 4, 2007
  5. paulo
    Replies:
    9
    Views:
    686
    James Kanze
    Mar 6, 2009
Loading...

Share This Page