С++ char * [] to char ** преобразование
У меня есть этот простой код, который компилируется без ошибок/предупреждений:
void f(int&, char**&){}
int main(int argc, char* argv[])
{
f(argc, argv);
return 0;
}
И следующий аналогичный код, который не компилируется:
void f(int&, char**&){}
int main()
{
int argc = 2;
char* argv[] = { "", "", nullptr };
f(argc, argv);
//@VS2013 error: cannot convert argument 2 from 'char *[3]' to 'char **&'
//@GCC error: invalid initialization of non-const reference of type 'char**&' from an rvalue of type 'char**'
return 0;
}
Почему char*[]
может быть преобразован в char**&
в первом примере и не может быть преобразован во втором образце? Имеет ли значение, известно ли размер во время компиляции?
EDIT: Я думаю, что во втором случае требуется 2 конверсии, и только компилятор может сделать только одно неявное преобразование.
Этот код компилируется отлично:
void f(int&, char**&){}
int main()
{
int argc = 2;
char* temp[] = { "", "", nullptr };
char** argv = temp;
f(argc, argv);
return 0;
}
Ответы
Ответ 1
Комментарий Джефффри ссылается на стандарт, вот он:
4.2 Преобразование массива в указатель [conv.array]
Значение lvalue или rvalue типа "массив из N T" или "массив неизвестной границы T" можно преобразовать к значению типа "указатель на T". Результатом является указатель на первый элемент массива.
И значение prewue:
Значение prvalue ( "pure" rvalue) является выражением, которое идентифицирует временную объект (или его подобъект) или значение, не связанное с каким-либо объект.
Вы не можете привязать неконстантную ссылку к временному.
int& i = int(); // error
char* argv[] = { "", "", nullptr };
// the result of the conversion is a prvalue
char**& test = argv; // error
Поэтому следующий код будет с удовольствием компилироваться:
#include <iostream>
void f(int& argc, char** const& argv){
std::cout << argv[0] << std::endl; // a
}
int main()
{
int argc = 2;
char* argv[] = { "a", "b", nullptr };
f(argc, argv);
return 0;
}
Одна важная вещь, о которой я застеклен, отмечена в комментаре Kanze.
В первом примере, представленном в OP, char* argv[]
и char** argv
эквивалентны. Следовательно, преобразование отсутствует.
std::cout << std::is_array<decltype(argv)>::value << std::endl; // false
std::cout << std::is_array<char**>::value << std::endl; // false
std::cout << std::is_array<char*[]>::value << std::endl; // true
std::cout << std::is_same<decltype(argv), char**>::value << std::endl; // true
std::cout << std::is_same<decltype(argv), char*[]>::value << std::endl; // false
Ответ 2
Потому что, несмотря на внешность, второй аргумент main
имеет
тип char**
. При использовании в качестве объявления функции
аргумент, массив верхнего уровня переписывается указателю, поэтому char
*[]
, по сути, char**
. Это относится только к функции
параметры, однако.
A char*[]
(как в вашем втором случае) может преобразовать в char**
,
но результаты преобразования (как и при любом преобразовании) являются
rvalue и не может использоваться для инициализации неконстантной ссылки.
Почему вы хотите ссылку? Если нужно изменить указатель,
изменение аргумента char**
до main
- это поведение undefined
(формально, в C, по крайней мере, я не проверял, больше ли С++
либерал здесь). И, конечно же, вы никак не можете
изменить постоянный адрес массива. И если вы не хотите
изменить его, зачем использовать ссылку?
Ответ 3
Тип temp
в
char* temp[] = { "", "", nullptr };
не char * [], it
char*[3]
Последнее не может быть неявно преобразовано в ` char ** '.
В main
тип argv
- это несвязанный массив char*
, который эквивалентен char**
Я допускаю, что это запутывает:)