简体   繁体   中英

Understanding const correctness with conflicting requirements

Error: Invalid conversion from 'char**' to 'const char**'

Similar questions did not appear to have the same set of circumstances (ie two functions with different const requirements on the same structure). Please only mark this as a duplicate if it genuinely is.

ROS/C++: http://wiki.ros.org/ROS/Tutorials

Argument Parser: https://github.com/jamolnng/argparse

I need to pass argv to a function from ROS and a function from the argparse header. The former takes a char** the latter takes a const *char[] .

Code example (basic patterns are just taken from the examples for both libraries):

int main(int argc, char **argv){
  argparse::ArgumentParser parser("Parser");

  parser.add_argument()
        .names({"-v", "--video"})
        .description("Enable video output for this node.")
        .required(false);
  parser.enable_help();

  //Problem 1: requires argv to be "const *char[]"
  auto err = parser.parse(argc, argv); 
  if (err){ /*error handling*/}

  //Problem 2: requires argv to be non-const
  ros::init(argc, argv, "node_name");

  ...

  return 0;
}

I need to call both of these functions but they both require different types for the same structure. Function prototypes for clarity:

//Declaration 1
Result parse(int argc, const char *argv[]);

//Declaration 2
void ros::init (int & argc,
                char **argv,
                const std::string &name,
                uint32_t options = 0 
                );
  1. Is there a way I can call both functions?

  2. Why is this even a problem? So far as I understand the const in Declaration 1 is just a promise that the function parse() will not modify the argv ; why does this need the variable to be const in the calling scope ( https://isocpp.org/wiki/faq/const-correctness ).

Edit - further information: On a hunch I tested a minimum working example without reference to either ROS or the argparsing libarary. Here is the test code:

#include <iostream>

void f(const char **a){
  std::cout << a[1] << std::endl;
}

int main(int argc, char **argv){
  // Attempt 1
  f(argv); //Causes compilation error.

  // Attempt 2
  f(const_cast<const char**>(argv)); //No compilation error and correct functionality

  return 0;
}

I further checked that the const_cast resulted in the behaviour I desired (constness for the duration of the call to f() and no more). Adapting Attempt 2 to the original problem solved my problem and I will add an answer below.

I am okay with the const_cast here because I am elevating to const and not trying to work around a data-structure which shouldn't be modified. That said I don't like const_cast and I don't understand why it appears to be necessary in this case. I will leave the question open to see if anyone is willing to explain this (answer question 2 above), I'll post my functional solution.

This is a functional solution but I won't accept it as the answer until I have an explanation as to why this works or why this appeared to be necessary.

I changed:

auto err = parser.parse(argc, argv);

to

auto err = parser.parse(argc, const_cast<const char**>(argv));

Isolated testing (see edit) showed that this generated the behaviour I wanted and as far as I know is safe in this specific context. I do not think this is a "good" solution. I feel like const_cast is a major code smell but I don't know enough to say why it would be bad in this case.

This solution also required the linking flag -lstdc++fs at compile time.

If you're using C++17 you can also try std::as_const() but I'm limited to C++11 at the moment.

First, I would think why non-const can't be implicitly converted to const: that would be unreliable, for instance, when deciding which overload to call. The contrary implicitly conversion is ok.

const_cast is also for adding constness, not only for removing it. Then, const_cast also helps to call a specific overload like in your case. I don't think it's a bad solution.

void f(char **a) {};
void f(const char *a[]) {};

int main(int argc, char **argv){
  f(argv); //calls first f
  f(const_cast<const char**>(argv)); //calls second f
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM