Use of const for non-reference parameters

Discussion in 'C++' started by Urs Thuermann, Jun 16, 2011.

  1. I'm working on a project that uses const for function parameters that
    are neither references nor pointers, i.e. something like this:

    int foo(const int a) { ... }
    int bar(const string b) { ... }

    I don't see why this is useful. Since the argument is copied to the
    called function it doesn't matter to the caller whether the argument
    is modified or not. So why would one want to write code like that?

    urs
    Urs Thuermann, Jun 16, 2011
    #1
    1. Advertising

  2. Urs Thuermann

    Ian Collins Guest

    On 06/16/11 08:53 PM, Urs Thuermann wrote:
    > I'm working on a project that uses const for function parameters that
    > are neither references nor pointers, i.e. something like this:
    >
    > int foo(const int a) { ... }
    > int bar(const string b) { ... }
    >
    > I don't see why this is useful. Since the argument is copied to the
    > called function it doesn't matter to the caller whether the argument
    > is modified or not. So why would one want to write code like that?


    It doesn't make a lot of sense. Maybe they forgot the &?

    --
    Ian Collins
    Ian Collins, Jun 16, 2011
    #2
    1. Advertising

  3. Urs Thuermann

    Balog Pal Guest

    "Urs Thuermann" <>
    > I'm working on a project that uses const for function parameters that
    > are neither references nor pointers, i.e. something like this:
    >
    > int foo(const int a) { ... }
    > int bar(const string b) { ... }
    >
    > I don't see why this is useful.


    Usefulness is the same as for any 'const' marker. The implementer of the
    function decided b shall not change, express it by saying const and use the
    language to reject actual modification attempts. It also makes it easy to
    know at any point of the function that the input parameter is the same as at
    call.

    > Since the argument is copied to the
    > called function it doesn't matter to the caller whether the argument
    > is modified or not.


    Yep. To the caller it is not interesting. So when you declare the above
    function it should be

    int bar(string b);

    and const placed only at the definition. In declarations the top-level const
    is ignored so those are the same.

    However it is a thing not widely known by programmers. So have the tendency
    to confuse humans and even tools. For those practical reasons many
    guidelines advise agains const-ing the function params even if otherwise
    tell to make everything const. At the same time they may advise to not
    mutate the params, or limit mutation to the function preamble.

    It's a judgement call what you rather sacrifice.
    Balog Pal, Jun 16, 2011
    #3
  4. On 16 juin, 10:53, Urs Thuermann <> wrote:
    > I'm working on a project that uses const for function parameters that
    > are neither references nor pointers, i.e. something like this:
    >
    >         int foo(const int a) { ... }
    >         int bar(const string b) { ... }
    >
    > I don't see why this is useful.  Since the argument is copied to the
    > called function it doesn't matter to the caller whether the argument
    > is modified or not.  So why would one want to write code like that?


    Some coding standards forbid modifying the parameters - passed by
    value - of a function. The reason behind is that it makes debugging
    easier in case of analysis in a debugger (typically a post morten
    analysis of a core dump).

    This may be an enforcement of that rule. Personnaly, I try to stick to
    it but I don't bother with making parameters const; leaking a coding
    practice into the interface is quite distracting (and it is more
    typing).

    --
    Michael
    Michael DOUBEZ, Jun 16, 2011
    #4
  5. On Jun 16, 12:53 pm, Urs Thuermann <> wrote:
    > I'm working on a project that uses const for function parameters that
    > are neither references nor pointers, i.e. something like this:
    >
    >         int foo(const int a) { ... }
    >         int bar(const string b) { ... }
    >
    > I don't see why this is useful.  Since the argument is copied to the
    > called function it doesn't matter to the caller whether the argument
    > is modified or not.  So why would one want to write code like that?
    >
    > urs


    Hi

    May be, one minor use of const argument in pass-by-value is
    because of coding standard and some self-document programs.
    In the following declaration:
    int foo(const int a);
    we explicitly say, although 'a' is passed by value and the caller
    passes
    a copy of it, but we actually need to a value and no one
    suppose to change it.

    My two cents,
    -- Saeed Amrollahi
    Saeed Amrollahi, Jun 16, 2011
    #5
  6. "Urs Thuermann" wrote in message
    news:...
    >
    >I'm working on a project that uses const for function parameters that
    >are neither references nor pointers, i.e. something like this:
    >
    >int foo(const int a) { ... }
    >int bar(const string b) { ... }
    >
    >I don't see why this is useful. Since the argument is copied to the
    >called function it doesn't matter to the caller whether the argument
    >is modified or not. So why would one want to write code like that?
    >
    >urs


    As said already by others, it makes sense in the function body just like any
    other const declaration.
    For prototype declarations it makes less sense. It is superfluous (just
    like the a and the b).
    Sometimes prototypes are generated automatically. Such generators may copy
    the const to the prototype. So that may be a reason why you see const in
    prototypes. Further, I have seen one compiler which issued a warning if the
    const was missing in the prototype, but present in the full function
    definition. That may be another reason why you see it in prototypes.
    Fred Zwarts \(KVI\), Jun 16, 2011
    #6
  7. Urs Thuermann

    none Guest

    In article <>,
    Ian Collins <> wrote:
    >On 06/16/11 08:53 PM, Urs Thuermann wrote:
    >> I'm working on a project that uses const for function parameters that
    >> are neither references nor pointers, i.e. something like this:
    >>
    >> int foo(const int a) { ... }
    >> int bar(const string b) { ... }
    >>
    >> I don't see why this is useful. Since the argument is copied to the
    >> called function it doesn't matter to the caller whether the argument
    >> is modified or not. So why would one want to write code like that?

    >
    >It doesn't make a lot of sense. Maybe they forgot the &?
    >


    It is not needed in the interface definition (albeit not harmful per
    se) but in implementation:

    int foo(const int a)
    {
    //... use a without modifying it
    }

    is comparable to:

    int foo()
    {
    const int a = getAFromSomewhere();
    // ... use a without modifying it
    }

    The const, albeit less important than a const& parameter that
    documents the interface can still be useful.
    none, Jun 16, 2011
    #7
  8. "Balog Pal" <> writes:

    > Yep. To the caller it is not interesting. So when you declare the
    > above function it should be
    >
    > int bar(string b);
    >
    > and const placed only at the definition. In declarations the top-level
    > const is ignored so those are the same.


    Oh, that's new to me. I thought I have once tried

    int bar(string b);
    int bar(const string b) { ... }

    and have got a compiler error because of prototype mismatch. And I
    have seen the const in header files where I disliked it. OK, now I
    have tried again and I see I was false on that. The above works
    perfectly and then I see it might actually make sense to have the
    const in the defintion only, e.g. to make debugging easier as pointed
    out in this thread by others.

    > However it is a thing not widely known by programmers. So have the
    > tendency to confuse humans and even tools. For those practical
    > reasons many guidelines advise agains const-ing the function params
    > even if otherwise tell to make everything const. At the same time
    > they may advise to not mutate the params, or limit mutation to the
    > function preamble.


    I was unaware of this also. But neither that nor the existence of
    tools that have problems with it would in my eyes count as a reason
    not to use it: tools can be fixed, programmers should learn the
    language. Like I have done a little bit more now.

    Thanks
    urs
    Urs Thuermann, Jun 16, 2011
    #8
  9. Urs Thuermann

    Marc Guest

    Urs Thuermann wrote:

    > "Balog Pal" <> writes:
    >
    >> Yep. To the caller it is not interesting. So when you declare the
    >> above function it should be
    >>
    >> int bar(string b);
    >>
    >> and const placed only at the definition. In declarations the top-level
    >> const is ignored so those are the same.

    >
    > Oh, that's new to me. I thought I have once tried
    >
    > int bar(string b);
    > int bar(const string b) { ... }
    >
    > and have got a compiler error because of prototype mismatch. And I
    > have seen the const in header files where I disliked it. OK, now I
    > have tried again and I see I was false on that. The above works
    > perfectly and then I see it might actually make sense to have the
    > const in the defintion only, e.g. to make debugging easier as pointed
    > out in this thread by others.


    Note that Oracle's compiler has a bug there where it does not ignore
    the const. And since this is part of the ABI, they cannot fix it.
    Marc, Jun 16, 2011
    #9
  10. On Jun 16, 4:53 am, Urs Thuermann <> wrote:
    > I'm working on a project that uses const for function parameters that
    > are neither references nor pointers, i.e. something like this:
    >
    >         int foo(const int a) { ... }
    >         int bar(const string b) { ... }
    >
    > I don't see why this is useful.  Since the argument is copied to the
    > called function it doesn't matter to the caller whether the argument
    > is modified or not.  So why would one want to write code like that?


    There is a potential performance penalty with your second example
    (const heavy-weight-type). Consider this slight modification of your
    example bar:

    string bar(const string b);

    If this function does nothing but modify a copy of b and return it,
    then this is seriously prematurely pessimized, especially in C++11:

    string bar(const string b) // bad
    {
    string x(b);
    x += "more text";
    return x;
    }

    This should instead be written:

    string bar(string b) // good
    {
    b += "more text";
    return b;
    }

    When the argument to bar is an lvalue string, the first implementation
    does 2 copies and 1 move. The second implementation will do 1 copy
    and 1 move.

    When the argument to bar is an rvalue string, the first implementation
    does 1 copy and 1 move. The second implementation will do 0 copies
    and 1 move.

    Either way, the first implementation is always too expensive by 1
    copy.

    Howard
    Howard Hinnant, Jun 16, 2011
    #10
  11. On 16 juin, 18:50, Howard Hinnant <> wrote:
    > On Jun 16, 4:53 am, Urs Thuermann <> wrote:
    >
    > > I'm working on a project that uses const for function parameters that
    > > are neither references nor pointers, i.e. something like this:

    >
    > >         int foo(const int a) { ... }
    > >         int bar(const string b) { ... }

    >
    > > I don't see why this is useful.  Since the argument is copied to the
    > > called function it doesn't matter to the caller whether the argument
    > > is modified or not.  So why would one want to write code like that?

    >
    > There is a potential performance penalty with your second example
    > (const heavy-weight-type).  Consider this slight modification of your
    > example bar:
    >
    >    string bar(const string b);
    >
    > If this function does nothing but modify a copy of b and return it,
    > then this is seriously prematurely pessimized, especially in C++11:
    >
    >    string bar(const string b)  // bad
    >    {
    >         string x(b);
    >         x += "more text";
    >         return x;
    >    }
    >
    > This should instead be written:
    >
    >    string bar(string b)  // good
    >    {
    >         b += "more text";
    >         return b;
    >    }
    >
    > When the argument to bar is an lvalue string, the first implementation
    > does 2 copies and 1 move.  The second implementation will do 1 copy
    > and 1 move.
    >
    > When the argument to bar is an rvalue string, the first implementation
    > does 1 copy and 1 move.  The second implementation will do 0 copies
    > and 1 move.
    >
    > Either way, the first implementation is always too expensive by 1
    > copy.


    Are you sure of that ? Did you observe it on a compiler ?

    Because I would say:
    - In the first case, the caller reserves the size of one string on
    the stack then put a copy of the string in parameter. The called
    function then makes a copy of the parameter in the placeholder of the
    value returned and then apply the append.
    - In the second state, the caller does exactly the same (he has no
    way of knowing the parameter is the value returned). The caller
    modifies its parameter and then copy it to the return placeholder.

    In fact, with this interpretation, the second solution is less
    efficient because more data are copied.

    If the compiler can move, there will be in both case a move and an
    append (maybe not in the same order).

    In case of inlining, I expect the generated code will be exactely the
    same.

    Yet, this is pure speculation because compiler have ways to optimise
    code beyond our abilities.

    --
    Michael
    Michael DOUBEZ, Jun 16, 2011
    #11
  12. On Jun 16, 4:22 pm, Michael DOUBEZ <> wrote:
    > On 16 juin, 18:50, Howard Hinnant <> wrote:
    >
    >
    >
    >
    >
    > > On Jun 16, 4:53 am, Urs Thuermann <> wrote:

    >
    > > > I'm working on a project that uses const for function parameters that
    > > > are neither references nor pointers, i.e. something like this:

    >
    > > >         int foo(const int a) { ... }
    > > >         int bar(const string b) { ... }

    >
    > > > I don't see why this is useful.  Since the argument is copied to the
    > > > called function it doesn't matter to the caller whether the argument
    > > > is modified or not.  So why would one want to write code like that?

    >
    > > There is a potential performance penalty with your second example
    > > (const heavy-weight-type).  Consider this slight modification of your
    > > example bar:

    >
    > >    string bar(const string b);

    >
    > > If this function does nothing but modify a copy of b and return it,
    > > then this is seriously prematurely pessimized, especially in C++11:

    >
    > >    string bar(const string b)  // bad
    > >    {
    > >         string x(b);
    > >         x += "more text";
    > >         return x;
    > >    }

    >
    > > This should instead be written:

    >
    > >    string bar(string b)  // good
    > >    {
    > >         b += "more text";
    > >         return b;
    > >    }

    >
    > > When the argument to bar is an lvalue string, the first implementation
    > > does 2 copies and 1 move.  The second implementation will do 1 copy
    > > and 1 move.

    >
    > > When the argument to bar is an rvalue string, the first implementation
    > > does 1 copy and 1 move.  The second implementation will do 0 copies
    > > and 1 move.

    >
    > > Either way, the first implementation is always too expensive by 1
    > > copy.

    >
    > Are you sure of that ? Did you observe it on a compiler ?


    Did you?

    #include <iostream>

    struct string
    {
    string() {}
    string(const string&) {std::cout << "copy construct\n";}
    string& operator=(const string&) {std::cout << "copy assign\n";
    return *this;}
    string(string&&) {std::cout << "move construct\n";}
    string& operator=(string&&) {std::cout << "move assign\n"; return
    *this;}

    void operator+=(const char*) {}
    };

    #if SLOW

    string bar(const string b)
    {
    string x(b);
    x += "more text";
    return x;
    }

    #else

    string bar(string b)
    {
    b += "more text";
    return b;
    }

    #endif

    int main()
    {
    std::cout << "test lvalue\n";
    string s;
    string s1 = bar(s);
    std::cout << "\ntest rvalue\n";
    string s2 = bar(string());
    }

    $ clang++ -std=c++0x -stdlib=libc++ -DSLOW test.cpp
    $ a.out
    test lvalue
    copy construct
    copy construct

    test rvalue
    copy construct

    $ clang++ -std=c++0x -stdlib=libc++ test.cpp
    $ a.out
    test lvalue
    copy construct
    move construct

    test rvalue
    move construct

    > Yet, this is pure speculation because compiler have ways to optimise
    > code beyond our abilities.


    <cough>

    Howard
    Howard Hinnant, Jun 16, 2011
    #12
  13. Urs Thuermann wrote:

    > I'm working on a project that uses const for function parameters that
    > are neither references nor pointers, i.e. something like this:
    >
    > int foo(const int a) { ... }
    > int bar(const string b) { ... }
    >
    > I don't see why this is useful. Since the argument is copied to the
    > called function it doesn't matter to the caller whether the argument
    > is modified or not. So why would one want to write code like that?
    >
    > urs


    See http://stackoverflow.com/questions/1554750/c-const-keyword-use-
    liberally/1554762
    Johannes Schaub (litb), Jun 17, 2011
    #13
  14. Howard Hinnant wrote:

    > On Jun 16, 4:22 pm, Michael DOUBEZ <> wrote:
    >> Are you sure of that ? Did you observe it on a compiler ?

    > Did you?

    [...]
    >> Yet, this is pure speculation because compiler have ways to optimise
    >> code beyond our abilities.


    > $ clang++ -std=c++0x -stdlib=libc++ -DSLOW test.cpp


    I don't know this compiler, but shouldn't you enable all possible
    optimizations when talking about performance and optimization?

    > $ a.out
    > test lvalue
    > copy construct
    > copy construct
    >
    > test rvalue
    > copy construct
    > [...]


    This seems to be the expected behavior of unoptimized code, without any
    performance data.

    Gerhard
    Gerhard Fiedler, Jun 17, 2011
    #14
  15. Urs Thuermann

    Balog Pal Guest

    "Gerhard Fiedler" <>
    >> $ a.out
    >> test lvalue
    >> copy construct
    >> copy construct
    >>
    >> test rvalue
    >> copy construct
    >> [...]

    >
    > This seems to be the expected behavior of unoptimized code, without any
    > performance data.


    And of optimized code too, because constructors gained observable behavior
    emitting the text that the compiler is not allowed to kill in this context.
    As usual it is easy to forge an experiment to "prove" whatever point.
    Balog Pal, Jun 17, 2011
    #15
  16. Am 17.06.2011 18:40, schrieb Balog Pal:
    > "Gerhard Fiedler" <>
    >>> $ a.out
    >>> test lvalue
    >>> copy construct
    >>> copy construct
    >>>
    >>> test rvalue
    >>> copy construct
    >>> [...]

    >>
    >> This seems to be the expected behavior of unoptimized code, without any
    >> performance data.

    >
    > And of optimized code too, because constructors gained observable
    > behavior emitting the text that the compiler is not allowed to kill in
    > this context. As usual it is easy to forge an experiment to "prove"
    > whatever point.


    The observable behaviour of copy constructors can be omitted.

    --
    Thomas
    Thomas J. Gritzan, Jun 17, 2011
    #16
  17. On Jun 17, 9:40 am, "Balog Pal" <> wrote:
    > "Gerhard Fiedler" <>
    >
    > >> $ a.out
    > >> test lvalue
    > >> copy construct
    > >> copy construct

    >
    > >> test rvalue
    > >> copy construct
    > >> [...]

    >
    > > This seems to be the expected behavior of unoptimized code, without any
    > > performance data.

    >
    > And of optimized code too, because constructors gained observable behavior
    > emitting the text that the compiler is not allowed to kill in this context.
    > As usual it is easy to forge an experiment to "prove" whatever point.


    As "Thomas J. Gritzan" said tersely else-thread, there is an explicit
    provision in the standard, apart from the "as if" rule, that allows a
    compiler in some situations to omit copies and copy constructor calls
    and destructor calls, even when the program's observable execution
    changes as a result. See
    12.8 Copying class objects / 15
    Joshua Maurice, Jun 17, 2011
    #17
  18. On Jun 17, 10:41 am, Gerhard Fiedler <> wrote:
    > Howard Hinnant wrote:
    > > On Jun 16, 4:22 pm, Michael DOUBEZ <> wrote:
    > >> Are you sure of that ? Did you observe it on a compiler ?

    > > Did you?

    > [...]
    > >> Yet, this is pure speculation because compiler have ways to optimise
    > >> code beyond our abilities.

    > > $ clang++ -std=c++0x -stdlib=libc++ -DSLOW test.cpp

    >
    > I don't know this compiler, but shouldn't you enable all possible
    > optimizations when talking about performance and optimization?
    >
    > > $ a.out
    > > test lvalue
    > > copy construct
    > > copy construct

    >
    > > test rvalue
    > > copy construct
    > > [...]

    >
    > This seems to be the expected behavior of unoptimized code, without any
    > performance data.


    You are correct that I should have posted -O3 compiled results. I
    happen to know my compiler well enough to know that constructor
    elision is the same for all optimization levels. The results are the
    same as what I posted no matter what for this compiler.

    Furthermore these results reflect all legal constructor elisions being
    taken advantage of. I.e. no compiler could elide more and remain
    conforming.

    Howard
    Howard Hinnant, Jun 18, 2011
    #18
  19. Urs Thuermann

    Balog Pal Guest

    "Thomas J. Gritzan" <>
    > The observable behaviour of copy constructors can be omitted.


    Yeah, for the temporary created during copy-initialization. Not for anything
    else, especially named objects, like in this case.
    Balog Pal, Jun 18, 2011
    #19
  20. Urs Thuermann <> wrote:
    > int foo(const int a) { ... }
    > int bar(const string b) { ... }
    >
    > I don't see why this is useful.


    I am one of those few programmers who think that constness should be
    the *default*, and non-constness the exception (iow. only used when you
    truly need the variable to be non-const). That is, every time I declare
    a variable, I make it const unless that variable needs to be changed.
    While the usefulness of this is admittedly somewhat low, it still serves
    as both documentation ("this variable will not be modified during the
    rest of this function") and minor code correctness checking (if a
    variable is intended to not to be modified, if you get an error that
    you are in fact trying to modify, then you either have a design error
    in your code, or you need to "re-document" your variable declaration).
    It is also remotely possible that declaring non-changing variables as
    const might in a few cases help the compiler to optimize better (although
    I don't have any concrete evidence of this right now).

    I have to admit, though, that I seldom declare non-changing function
    parameters const because it makes the function declarations longer
    (especially for functions taking many parameters). I have been slowly
    changing this, though. (After all, brevity should never be preferred
    over quality.)
    Juha Nieminen, Jun 18, 2011
    #20
    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. Mark Stijnman
    Replies:
    2
    Views:
    467
    =?ISO-8859-15?Q?Juli=E1n?= Albo
    Apr 22, 2005
  2. Andrew Ward
    Replies:
    2
    Views:
    1,038
    Zorro
    Jul 19, 2005
  3. Javier
    Replies:
    2
    Views:
    549
    James Kanze
    Sep 4, 2007
  4. George2
    Replies:
    10
    Views:
    581
    Pete Becker
    Dec 17, 2007
  5. fungus
    Replies:
    13
    Views:
    874
    fungus
    Oct 31, 2008
Loading...

Share This Page