3

I have little C++ experience. My question is more about confirming if I'm thinking correctly with some queries. I was trying to understand a code and it has the following line:

string ltrim(const string &);

I saw the following answers: 1. What is the difference between "std::string const &s" and "const std::string &s"? 2. https://www.geeksforgeeks.org/stdstring-class-in-c/ 3. https://codereview.stackexchange.com/questions/32842/passing-parameters-by-reference

I know the concept of call by value and call by reference.

  1. What is 'ltrim'?

It seems to me that it is a function name declared here because I know C++ doesn't have a predefined function with this name. Am i correct to assume that it means that it is a function named 'ltrim' of type 'string' and this function has a parameter 'a constant string type'.

However, usually we have functions like:

void abc (int &a)

where a is a variable. What is the variable in:

string ltrim(const string &);

because 'const' has a predefined meaning and 'string' is a datatype. What is the variable passed in the parameter?

  1. Also, if there is no variable, what does '&' do here. I understand call by reference some what w.r.t C++, but I don't see a variable name, I don't know what s being used to pass.

I tried looking on internet for some information but i couldn't narro down to a place which explains this in terms of string.

Sulphur
  • 514
  • 6
  • 24

4 Answers4

5

What is 'ltrim'?

Your guess is right, ltrim (left-trim) is a function accepting a const std::string& as parameter and returning a std::string. We don't actually need to give the parameter a name like you're used to seeing because we don't use the parameter yet. We'll only use it once we define the function body, the current line of code is only a declaration to the function.

Also, if there is no variable, what does '&' do here. I understand call by reference some what w.r.t C++, but I don't see a variable name, I don't know what s being used to pass.

Just because there's no variable name doesn't mean the function can't ask for a reference to a certain type. A reference type is part of the type, not of the name of the variable, the name of the parameter is completely optional:

void Foo(int, const int&, int*)
{
  // stuff..
}

int main()
{
  Foo(1, 2, nullptr);
}

This code is perfectly legal as long as Foo doesn't try to access the parameters. It can't because they have no name.


Splitting the declaration and definition and giving a parameter a name in one and not in the other is perfectly valid too:

void Foo(int);

int main()
{
  Foo(1);
}

void Foo(int bar)
{
  // stuff with 'bar'...
}
Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • One interesting thing here is that in a definition (not a declaration) a parameter with no name indicates an unused parameter. This seems a little superfluous to say since, of course it's unused because it can't be, it has no name! But this is kind of an important detail to remember because it can be used to hush up compiler warnings about unused parameters. It's telling the compiler that the parameter is unused, and that you know it's unused and intended it to not be used, so it shouldn't warn. – Omnifarious Jun 20 '19 at 22:30
3
string ltrim(const string &);

This line of code is declaring that a function named ltrim exists, that it accepts an unnamed const string& parameter, and that it returns a string.

The confusing part is that C++ does not require names for function parameters! This is something you will see when an interface needs to be preserved but the implementation no longer uses the parameter.

Romen
  • 1,617
  • 10
  • 26
3

The purpose of a function prototype in C++ is to tell the compiler

  • what the name of the function is,
  • what the types of its arguments are, and
  • what type (if any) it returns.

An important detail here is that the above list does not include the names of the parameters. For example, all of the following pieces of code declare the same function:

int myFunction(int a, double b);
int myFunction(int apples, double bananas);
int myFunction(int pizkwat, double zyzzyzyplyz);

Moreover, the actual implementation of myFunction wouldn't be required to use any of the names given here. For example, we could write

int myFunction(int snickerdoodle, double alfajores) {
    // ... do something ... //
}

and the compiler would be perfectly happy with it, even though none of the above prototypes use those names.

Now, for a weird bit of C++ trivia: in C++, it's possible to declare functions that have parameters that don't have names. For example, this code is perfectly legal:

void doSomething(int) {
   // This function takes in an integer. You can't call it without
   // passing in that integer. However, this function has no way of
   // referencing its argument, because it doesn't have a name!
}

You do see this used in practice. For example, overloading the postfix ++ operator requires declaring a function named operator++ that takes in an int whose value is essentially never used. Or, you might be overriding a function in a derived class and not need to use the arguments provided.

This means that we have three rules in C++:

Rule One: The names of parameters in function prototypes are ignored.

Rule Two: A function implementation doesn't have to pick the same names for its parameters as its prototype.

Rule Three: Parameters don't even need to have names at all.

By combining these three rules together, you sometimes see things like this:

int myFunction(int, double); // Prototype gives no names to arguments

int myFunction(int a, double b) {
    // .. use parameters a and b ... //
}

Here, the function prototype doesn't give names to its parameters, but that's okay! C++ still learns everything it needs to about the function (name, return type, and argument types). This is legal because of Rule One and Rule Three. In the implementation, which does actually need to reference the parameters, those parameters are given names a and b. The fact that these parameters now have names is fine due to Rule Two.

So, returning to your example, the function prototype

string ltrim(const string &);

means "I'm declaring a function named ltrim. It takes in a reference to a const string, and it returns a string." It's completely equivalent to saying

string ltrim(const string& toTrim);

or

string ltrim(const string& oompaLoompa);

The actual implementation of ltrim, by the looks of it, almost certainly names its parameter so that it can reference it and trim the string.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    You're an oompaLoompa – Hatted Rooster Jun 20 '19 at 18:38
  • I saw this late but this is an excellent explanation! Thank you. – Sulphur Jun 20 '19 at 18:51
  • The case for including the variable name in a prototype, or having a name but commenting it out in an implementation, is it, if well named, documents what the variable will be (or would have been) used for. Sometimes a function prototype in a header is going to be the only worthwhile documentation you're going to get for how to use the function, so it's really nice if it is complete and descriptive. – user4581301 Jun 20 '19 at 19:14
1

That line declares a function named ltrim that returns a string and accepts a single parameter: a reference to a const string.

The name for a parameter is optional. In this case, it's likely that the function definition has a name for that parameter, but it can be omitted there as well. If a parameter is unnamed in the function definition then there's no way to refer to that parameter in the function body, but it can be useful if you need to accept a parameter to fulfill some interface but don't actually need to use it.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52