Нечетное 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, поэтому не жалуется.