0

I was working on a piece of code.

struct Argument
        {
                Argument(): s_name(""), name(""), optional(true) {}
                Argument(String& s_name_inp,  String& name_inp, bool optional_inp):s_name(s_name_inp),name(name_inp),optional(optional_inp){}
        .....More code.....
        }

Somewhere later in the code:

void addArgument(String& name_inp,bool optional=true)
    {
            String name;
            //Creating a tmep string to store the corrected name if the user doesn't enter - or -- wrt. name of their argument.
            name = isDashed(name_inp) ? name_inp : addDash(name_inp) ;
            //using the dashed name to check if it's shortname or long name.

            if(name.size()>3)
            {
                    //This is the long name.
                    Argument arg("", name, optional);
                    insertArgument(arg);
            }
            else
            {
                    //This is for the short name
                    Argument arg(name, "", optional);
                    insertArgument(arg);
            }
    }

Both Struct Argument and fn addArgument are part of a class where struct Argument is defined in the private and addArgument in the public.

It throws up an error when I run the code.. For Long name one-

error: no instance of constructor "ArgumentParser::Argument::Argument" matches the argument list 
            argument types are: (const char [1], ArgumentParser::String, __nv_bool)

For Short Name one -

error: no instance of constructor "ArgumentParser::Argument::Argument" matches the argument list
            argument types are: (ArgumentParser::String, const char [1], __nv_bool)

I could figure out how to fix it. This error is coming because of the empty strings "" which I enter. Adding const in the struct Argument constructor fixes the problem.

struct Argument
        {
                Argument(): s_name(""), name(""), optional(true) {}
                Argument(const String& s_name_inp, const String& name_inp, bool optional_inp):s_name(s_name_inp),name(name_inp),optional(optional_inp){}
        .....More code.....
        }

Similarly, declaring a String blank = "" ; and passing it while initializing an obj of struct Argument instead of "" curbs the problem as well. Also, passing String instead of String& in the Argument constructor also solves the issue-

Argument(String s_name_inp,  String name_inp, bool optional_inp):s_name(s_name_inp),name(name_inp),optional(optional_inp){}

Thus what I concluded from this is that simply passing "" is a problem since it is not stored in some variable. It doesn't have some location which can be referenced(String&) in case a change is made inside the code. That's why adding a const before String& in the constructor ensures the compiler that no change will be made even though we are passing by ref, so the compiler allows the use of "".

However I do not understand why the compiler is being so 'smart' even though I haven't done any incorrect operation. It's like the compiler is popping bugs for 'security' also, apart from the usual 'errors' we make.


With this question, I also want to understand in a broader sense, the use of const.

I get that it's just for ensuring that no change will be made to the variable being passed(or returned) to a function/constructor. But why do we need it if I, as the programmer, can ensure that I won't be changing the variable. Why do we need const then?

One thing I know is, it can be used to tell others to keep a parameter unchanged if they see your code.(https://www.youtube.com/watch?v=4fJBrditnJU is a good source for learning about const) Also, what is the difference between use of const in these two cases -

int somefunc const(int& var)
{
 //   var=4; This isn't allowed due to the const in fn
    return var;
}

int somefunc(const int& var)
{
// var =4 ; This is also not allowed.
    return var;
}

Also, I want to know how passing by reference and passing by value differ if they are just being used for assignments, i.e, no change is being made to them but they are being used for checking conditions, or doing some assignments to other vars.

Adding const String& is like a surety that whatever the user is passing is not being tampered with, in the code, so can't we simply replace it with String? Because passing by value will instantiate a copy of the passed variable/parameter? Why do we use const String& then?


Another question about passing pointers and passing by reference: The only use I know of String&(or int& or any other) is to directly pass the actual 'thing' into the function, not a copy of it, so whatever we do with that 'thing' will be reflected on the original, just like we use pointers to get the changes to be made to the actual 'thing' and not a copy of it. Why don't we use pointers instead of passing by reference? What advantage does it bring?

I know this question is kind of vast but these are all interconnected questions. Answer to one compliments another. Thanks to anyone who takes the time to answer whatever they seems suitable!

t.niese
  • 39,256
  • 9
  • 74
  • 101
  • Could you provide a [mre]? – Alan Birtles Sep 25 '20 at 08:28
  • Don't ask multiple questions at once. If you have multiple ones, then create multiple. In the current form, most of these questions are individual not connected questions. – t.niese Sep 25 '20 at 08:30
  • 1
    `But why do we need it if I, as the programmer, can ensure that I won't be changing the variable` A programmer could also "ensure" to not make any mistakes in the written code, so why bother with compiler errors and warnings, and not just assume that the code is valid. – t.niese Sep 25 '20 at 08:38
  • Remember - the compiler is actually creating a temporary `std::string` using its `const char*` constructor. You can't modify a temporary object, so implicit conversion to a non-const reference is not allowed: https://stackoverflow.com/questions/1565600/how-come-a-non-const-reference-cannot-bind-to-a-temporary-object – parktomatomi Sep 25 '20 at 09:04
  • [Why would you use the keyword const if you already know variable should be constant?](https://stackoverflow.com/questions/18157523), [Use const wherever possible in C++?](https://stackoverflow.com/questions/22285773) – t.niese Sep 25 '20 at 09:07

2 Answers2

1

Pointers and l-value references are exchangeable, for the most part. It's just less to write at the site of invocation.

The difference between const & and & comes, as you noticed, from a simple reference requiring a variable to reference. A reference with & has the semantic of "let me write that down for you". A const & allows the creation of the temporary copy on-the-fly, and has the semantic of "let me have a look". Pass by value has the meaning of "give me a copy, I decide what to do with it".

In practical experience, avoiding copies is your primary concern. So call-by-value is something you want to avoid for anything bigger than an integral value.

Const-correctness is mostly just design of the C++ language, it's not strictly required from a technical point of view. You can consider it to be a way of saying "trust me, I'm not going to break it".

About passing temporary values to a simple & parameter, think about it for a moment who actually owns the temporary, and for how long it's going to exist. Anything written to it, if you would be allowed to do so, would be lost.

Also think about default parameters, e.g. void foo(const bar &foobar = bar(42)). These are never allowed to be non-const references, as it would result in undefined behavior. That default object may live in a static scope (rather than every caller creating it anew), and someone messing with it would result in changing defaults. Good luck ever finding the cause for that bug.

Even for non-default parameters, const & allows the creation of the temporary at compile time (constexpr constructors), and also folding multiple instances of identical temporaries into a single instance in memory. This optimization would likewise not be possible without the guarantees made by const.

There is a plethora of other cases where const-correctness is also the key to enable compiler optimizations. So it's generally better to use correctly, even if you assumed that your code discipline would had prevented at least undefined behavior.

Ext3h
  • 5,713
  • 17
  • 43
  • `In practical experience, avoiding copies is your primary concern. So call-by-value is something you want to avoid for anything bigger than an integral value.` that can't be said in general, that really depends on the situation. – t.niese Sep 25 '20 at 08:48
1

But why do we need it if I, as the programmer, can ensure that I won't be changing the variable. Why do we need const then?

You could also ensure to not make any errors in coding, so why compiler errors/warnings, unit tests, issue trackers, …

Adding const String& is like a surety that whatever the user is passing is not being tampered with, in the code, so can't we simply replace it with String? Because passing by value will instantiate a copy of the passed variable/parameter? Why do we use const String& then?

  • A class could be more expensive to copy then the indirections over the reference are. Especially if the compiler would be able to inline the function in case of a const & for which no indirection would happen at all.
  • A copy could introduce unwanted side effects (could be problematic in an environment with limited resources)
  • A class could have a deleted copy constructor so no copy would be possible at all, but you still want to ensure const correctness.

The only use I know of String&(or int& or any other) is to directly pass the actual 'thing' into the function, not a copy of it, so whatever we do with that 'thing' will be reflected on the original, just like we use pointers to get the changes to be made to the actual 'thing' and not a copy of it. Why don't we use pointers instead of passing by reference? What advantage does it bring?

A pointer can accept a nullptr so you need to handle the case where nullptr is passed (if no nullptr must be passed gsl::not_null could be used). But using pointers would not allow passing temporaries.

t.niese
  • 39,256
  • 9
  • 74
  • 101