How to pass entire collection(char**) of command line arguments as read-only in c++?
Asked Answered
F

1

7

Suppose I have a program like this:

#include <iostream>
#include <string>
#include <vector>

// Takes values and outputs a string vector.
std::vector<std::string> foo(const int argc, char* args[]) {
    std::vector<std::string> strings;
    for (int i = 0; i < argc; i++)
        strings.push_back(args[i]);
    return strings;
}

int main(int argc, char *args[]) {

    std::vector<std::string> strings = foo(argc, args);

    for (unsigned int i = 0; i < strings.size(); i++)
        std::cout << strings[i] << std::endl;

    return 0;
}

Where the takeaway is that I'm trying pass the main() function's char** argument to another function or class. (I understand there are better ways to achieve what the above program does, my question is about passing char** arguments as read-only).

Questions:

  1. I've found that I can't make the second foo() argument const like the first. Why is this? A char** can't be converted to a const char**?
  2. I want to pass in this argument as "read-only". I'm not sure how to go about this, if it were say a string I'd pass it in via const reference, but I'm not sure how to go about this with pointers?
Flavine answered 11/2, 2019 at 11:19 Comment(2)
the question would be better if you included what you tried and the error message you getTailband
A little OT, but if you want a vector of your command line arguments, you could create it like this: std::vector<std::string> args(argv+1, argv+argc); (argv+1 to skip the program name)Afterdamp
C
9

I've found that I can't make the second foo() argument const like the first. Why is this? A char** can't be converted to a const char**?

If it were allowed, you'd be likely to break const correctness. Consider this

char const *a = "literl";
char *b = nullptr;
char const **c = &b; // This is not allowed in C++. Like your code.
*c = a; // The qualifications match, but now it's b that points at a literal.
*b = 'L'; // Modifying a string literal! Oops!

So there is a good reason to disallow it as written. But that doesn't mean you can't do what you want at all. Qualification conversions are possible so long as they are stricter. Well that's the gist of it anyway.

I want to pass in this argument as "read-only". I'm not sure how to go about this, if it were say a string I'd pass it in via const reference, but I'm not sure how to go about this with pointers?

Pass a pointer to a const pointer to a const char. Better write it in code to explain:

                                                         // The const here
std::vector<std::string> foo(int const argc, char const* const args[]) {
    std::vector<std::string> strings;
    for (int i = 0; i < argc; i++)
        strings.push_back(args[i]);
    return strings;
}

Why is this allowed? Because if we equate it to the bad example I started with, c can no longer be used to assign to b, and so we cannot get into an erroneous state.

Crock answered 11/2, 2019 at 11:30 Comment(9)
Thankyou so much for the detailed response! I understand now it's disallowed. I'm still a tiny bit confused on the syntax of char const* const args[]. I seem to be able to get const char* const args[] and const char* const* args to compile fine. I understand that they're likely the same thing, but what I'm stuck on is the syntax of it and which term is controlling what. Would you mind very briefly explaining each component of char const* const args[] and what it's doing to make the variable read-only?Flavine
@Flavine - args is not immutable in either declaration. What is immutable, is what it's pointing at. So *args is const. We can't assign something invalid to it. And **args is const like you obviously wanted. Does that make more sense?Crock
Okay, that makes more sense, I think what confused me was the use of the const twice next to each other. I didn't know a "const pointer" was actually a thing!Flavine
@Flavine - A pointer is an object. So it can be declared const like any other object :)Crock
It's becoming a lot more clear now. One final question/request I have that would be really beneficial is a breakdown of the syntax of char const* const args[]. I'm used to putting const on the left of everything and am a little confused with the syntax. Would you mind marking which component matches what phrase in your sentence "Pass a pointer to a const pointer to a const char". Also it might just be my device but the // The const here is lining up almost in the middle of the two const keywords :PFlavine
@Flavine - char const* const args[] is exactly char const* const *args (I just didn't want to change your example too much). Now, read from right to left. args is a pointer to a const pointer to a const char. The qualifier applies to the thing on the left... except if there isn't anything there. For consistency, I always put it on the right when possible.Crock
I think there's a Q&A about the subject somewhere on SO too. You can look it up to learn more.Crock
Thanks very much, I completely understand now. :) I was just unclear syntactically with [] meaning *.Flavine
Here's the beautiful mathematical definition from the standard of allowed conversions (for some standard of beauty). Simply put (for some standard of simplicity): If you are going to add a const anywhere in e.g. char ****, then you must also add const in all following positions except the last: char **** -> char *const *** -> char *const *const *const *.Ligni

© 2022 - 2024 — McMap. All rights reserved.