Нечетное VS имя, искажающее поведение?

Рассмотрим следующий бесполезный код, который я компилирую как C++ на Win10 64-bit:

int test(int argc, char *argv[]);
int main(int argc, char *argv[])
{
   return test(argc, argv);
}

int test(int argc, char **argv)
{
   return 0;
}

Если весь этот код помещен в один и тот же файл.cpp, он правильно компилируется и связывается в VS2012, VS2013, VS2015 и mingw32-g++ v4.7.1, как и следовало ожидать.

Однако, если я просто перенесу определение тестовой функции в отдельный файл, полученные два файла все равно будут скомпилированы и правильно связаны с компилятором mingw, но на всех версиях VS я получу:

error LNK2019: unresolved external symbol "int __cdecl test(int,char * * const)" (?test@@YAHHQAPAD@Z) referenced in function _main"

Я могу решить эту проблему в VS, просто изменив объявление параметра argv в тестовой функции на char *argv[], но это не должно быть необходимым, так как char *argv[] а также char **argv означать то же самое, когда используется для объявления параметра.

Я не пробовал, но меня удивляет, будет ли VS считать две версии разными для перегрузки.

2 ответа

Решение

Да, это ошибка в схеме оформления имен Visual C++. Для параметров типа указателя константные и изменчивые квалификаторы верхнего уровня кодируются в оформленное имя, даже если они не относятся к типу функции. Так, например, char** а также char** const кодируются по-разному. (В вашем примере char*[] эквивалентно char** const.)

При определении способа оформления имени функции компилятор будет использовать первое объявление функции, даже если определение точно не соответствует первому объявлению. Вот почему ваш пример ссылается, когда определение находится в том же исходном файле, что и основная функция: функция test украшена именем, требуемым в первом объявлении, которое является тем же именем, на которое ссылается основная функция.

Если вы переместите и объявление, и определение в отдельный исходный файл, например,

int test(int argc, char *argv[]);

int test(int argc, char **argv)
{
   return 0;
}

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

Для отдельного файла используйте test (int argc, char ** const argv), основываясь на сообщении об ошибке. Обратите внимание, что адрес массива (char * argv[]) будет постоянным (поэтому argv будет постоянным), в отличие от указателя на указатель (char **argv). Хотя argv передается по значению, его нельзя изменить, поэтому я не уверен, почему VS так требователен.

Когда обе функции находятся в одном и том же файле, по-видимому, VS может обнаружить, что test () не изменяет argv, поэтому не жалуется.

Другие вопросы по тегам