5

I am a bit confused about one of the examples in my textbook. When the string is created, it is created as type string. However, when the same string is passed into a function, the function parameters are of const string and not string.

Here's the part of the code:

int main()
{
    string str;
    cout << "blah blah..";
    getline(cin, str);
    if (is_pal(str))
    //...
}

bool is_pal(const string& s)
{
    //...
}

Why is the function parameter const string& s instead of just string& s? I've read through my textbook but can't seem to find any explanation for this.

jxh
  • 69,070
  • 8
  • 110
  • 193
ruisen
  • 305
  • 3
  • 11
  • 2
    Maybe the string can be altered, but the function shouldn't be able to? – keyser Apr 27 '15 at 22:23
  • There are several functions that the string gets passed into, and several of those function do alter the string, such as removing punctuations, changing it to lower case, etc. – ruisen Apr 27 '15 at 22:25
  • But do they alter the string's original value (i.e. referencing it by memory address) or do they just take the parameter being passed and return a copy of it? – SteveK Apr 27 '15 at 22:26
  • This just indicates that the particular function named `is_pal` is not going to modify its argument. Read my explanation of `const` in C++: http://stackoverflow.com/a/3709257/103167 – Ben Voigt Apr 27 '15 at 22:26
  • I was searching for a duplicate, but failed. However, I found one question and nice answer that you may find close to the subject and interesting: http://stackoverflow.com/questions/3967177/when-to-use-const-and-const-reference-in-function-args – quetzalcoatl Apr 27 '15 at 22:31
  • Can you tell us why you think the function should accept non-`const` instead? – Lightness Races in Orbit Apr 27 '15 at 22:33
  • Oops, you are right, its a copy of the original string that is being modified and returned. – ruisen Apr 29 '15 at 18:14
  • Thanks for the replies everyone! I think the subtleties are a bit beyond what I can understand, but I think I have a good grasp of the basic idea now :) – ruisen Apr 29 '15 at 18:28

6 Answers6

9

Objects that may be expensive to copy, such as std::string, are very often passed by const lvalue reference in C++. This is a very common idiom; you will see it everywhere. A const lvalue reference can bind to both lvalues and rvalues without making any copies, so this is an efficient way to pass strings to functions that will not modify them.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
4

When a function uses const on its argument, it typically means the function will not alter the argument.

When you are writing your own functions, you should determine if the function intends to modify the argument or not, and use or not use const accordingly.

Likewise, when you are using functions that someone else has written, pay attention to whether or not the function intends to modify your object or not. This is made known to you if the function accepts a non-const reference.

void foo_will_not_modify (const std::string &x); // const ref - won't modify
void bar_will_not_modify (std::string x);        // copy - won't modify
void baz_will_modify (std::string &x);           // reference - can modify
jxh
  • 69,070
  • 8
  • 110
  • 193
4

Re

why is the function parameter const string& s instead of just string& s?

A main reason is that the latter can't bind to string literal or to an ordinary function result or result of string operators such as +, called an “rvalue”.

Well at least in standard C++, but Visual C++ allows that as an unfortunate language extension.

Another reason usually is that the author of the function thought it could be more useful when it promises to not modify its argument. Or at least, that that would make it easier to reason about code using it.


Example:

// To see the problem also with Visual C++, use that compiler's /Za option.
#include <iostream>
#include <string>
using namespace std;

void goodwrite( const string& s ) { cout << s << '\n'; }

void badwrite( string& s ) { cout << s << '\n'; }

auto main() -> int
{
    // Good:
    goodwrite( "The answer is " + to_string( 6*7 ) + "." );

    //! Uh oh, doesn't compile with standard C++:
    badwrite( "The answer is " + to_string( 6*7 ) + "." );
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • `const` reference or value copy parameters will accept computed temporary arguments (+1). – jxh Apr 27 '15 at 23:15
  • What do you mean by "the latter can't bind to string literal or to an ordinary function result or result of string operators such as +". Sorry, I'm a bit new to C++, so I'm not really familiar with the vocabulary. And what do you mean by compiler's /Za option? – ruisen Apr 29 '15 at 18:34
  • 1
    @ruisen: Effectively the thing about binding (making a reference refer to something) means you can't use an actual argument that is a string literal. And that you can't use an actual argument that is an ordinary function call or operator expression. Compiler options are used to modify the behavior of a compiler, e.g. make it follow the standard (which is what this one is about). They're specified in the invocation of the compiler. In an IDE you will typically instead tick off various options, or not, and then the IDE generates corresponding compiler options when it invokes the compiler. – Cheers and hth. - Alf Apr 30 '15 at 16:02
0

Passing a const reference means that the function will treat the argument as a constant, and will not modify it by any way. This allows calling this function with const values - either ones explicitly defined as consts, or implicit constants, such as string literals.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 1
    But -- importantly -- not only with const values. Passing a writeable string as the actual parameter to a formally `const` argument is quite legal. – Ben Voigt Apr 27 '15 at 22:29
  • Not to mention you can't pass an rvalue as a non-const reference argument so doing things like `is_pal("dddd")` won't work unless `is_pal` takes the string by `const` reference of by value. – Captain Obvlious Apr 27 '15 at 22:42
0

You can do this without the "const" keyword.

It is usually the best practice to use "const" keyword for parameters if your function is not modifying it. So, by mistake if you try to modify the parameters, it will shoe error.

It's just the best coding practice, I would suggest you also do the same if the function is not modifying the parameters.

Akash
  • 76
  • 4
0

TL;DR: to ensure that object isn't modified in function and to save one copy operation.


Only const methods may be called for const objects, and that methods couldn't alter state of an object:

class A {
    int i = 10;
    mutable int j = 10;         // Explicitly allowed to change even for const objects

public:
    void f() { ++i; }           // OK: non-const function changes object
    void g() const { ++i; }     // Error: const function changes object
    int h() const { return i; } // OK: const function doesn't change object
    int s() const { return ++j; } // OK: const function changes mutable field 
};

void foo(const A& a) {
    a.f();      // Error: only const methods are allowed
    a.g(); a.h(); a.j(); // OK
}

As you can see, there is no clean way to modify field i from function foo, but you can read A fields with h() and s() methods.

You can also ensure that your local object isn't modified by callee function by passing copy to it:

void foo(A a);
bool is_pal(std::string s);

But copying may be expensive, so you have to pass it by reference:

void foo(A& a);
bool is_pal(std::string& s);

And to ensure that object will have same state it had before calling your function you have to add const qualifier to it. This idiom is explained in Scott Meyers’ book, Effective C++, Third Edition, "Item 20: Prefer pass-by-reference-to-const to pass-by-value."

myaut
  • 11,174
  • 2
  • 30
  • 62