Понимание const правильности с противоречивыми требованиями - PullRequest
2 голосов
/ 31 марта 2020

Ошибка: Invalid conversion from 'char**' to 'const char**'

Подобные вопросы, по-видимому, не имеют одинакового набора обстоятельств (т.е. две функции с разными требованиями к константам в одной и той же структуре). Пожалуйста, отметьте это как дубликат, если это действительно так.

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

Анализатор аргументов: https://github.com/jamolnng/argparse

Мне нужно передать argv в функцию из ROS и функцию из заголовка argparse. Первый берет char**, второй - const *char[].

Пример кода (базовые c шаблоны только что взяты из примеров для обеих библиотек):

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 необходимо вызвать обе эти функции, но обе они требуют разных типов для одной и той же структуры. Прототипы функций для ясности:

//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. Есть ли способ, которым я могу вызвать обе функции?

  2. Почему это вообще проблема? Насколько я понимаю, const в Декларации 1 - это просто обещание, что функция parse() не изменит argv; почему для этой переменной в области вызова должна быть const (https://isocpp.org/wiki/faq/const-correctness).

Редактировать - дополнительная информация: на догадке я проверил минимальный рабочий пример без ссылки на ROS или библиотеку argparsing. Вот код теста:

#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;
}

Я также проверил, что const_cast привел к желаемому поведению (постоянство во время вызова на f() и не более). Адаптация попытки 2 к исходной проблеме решила мою проблему, и я добавлю ответ ниже.

Я согласен с const_cast здесь, потому что я поднялся до const и не пытаюсь обойти структуру данных, которая не должна изменяться. Тем не менее, мне не нравится const_cast, и я не понимаю, почему это кажется необходимым в этом случае. Я оставлю вопрос открытым, чтобы посмотреть, если кто-то захочет объяснить это (ответьте на вопрос 2 выше), я опубликую свое функциональное решение.

Ответы [ 2 ]

0 голосов
/ 31 марта 2020

Во-первых, я бы подумал, почему неконстантный неявно не может быть преобразован в констант: это было бы ненадежно, например, при решении, какую перегрузку вызывать. Напротив, неявное преобразование в порядке.

const_cast также для добавления константы, а не только для ее удаления. Затем const_cast также помогает вызвать специфическую c перегрузку, как в вашем случае. Я не думаю, что это плохое решение.

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
}
0 голосов
/ 31 марта 2020

Это функциональное решение, но я не приму его в качестве ответа, пока у меня не будет объяснения, почему это работает или почему это необходимо.

Я изменил:

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

до

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

Изолированное тестирование (см. Редактирование) показало, что это породило поведение, которое я хотел, и насколько я знаю, безопасно в этом конкретный c контекст. Я не думаю, что это "хорошее" решение. Я чувствую, что const_cast является основным запахом кода, но я не знаю достаточно, чтобы сказать, почему это будет плохо в этом случае.

Для этого решения также требовался флаг связи -lstdc ++ fs во время компиляции .

Если вы используете C ++ 17, вы также можете попробовать std :: as_const (), но сейчас я ограничен C ++ 11.

...