2013-12-09 5 views
14

У меня есть этот простой код, который компилируется без ошибок/предупреждений:C++ символ * [] на символ ** преобразования

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; 
} 
+0

Обратите внимание, что у вас есть больше проблем, чем что (связанные с совместимостью C++ 11), [см видеодемонстрацию здесь] (http://coliru.stacked-crooked.com/a/b27bbe925d88d735). – rubenvb

+3

В параметре функции 'char * name []' и 'char ** name' * эквивалентны * - они обозначают одно и то же (более конкретно, первое преобразуется во второе). – Xeo

+0

@rubenvb Я знаю, что const char * для char * устарел в C++ 11. Здесь также присутствуют другие проблемы с корректностью констант ... – Felics

ответ

3

Jefffrey's comment ссылается на стандарт, здесь:

4.2 Преобразование массива в указатель [conv.array]

Значение lvalue или rvalue типа «массив N T» или «массив неизвестной границы T» может быть преобразован в prvalue типа «указатель на T». Результатом является указатель на первый элемент массива .

И prvalue является:

prvalue («чистый» 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's comment.

В первом примере, представленном в ОП, 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 
+0

если main() имеет argv как prarmeter, то isnt, что преобразован в указатель, когда мы передаем его f()? –

+0

Это не говорит о переписании параметров функции, что является важной частью понимания происходящего. –

+0

@James В стандарте говорится, что происходит преобразование. Отличается ли он от того, как обрабатываются параметры функции? –

8

Потому что, несмотря на видимость, второй аргумент main имеет типа char**. Когда используется как объявление функции аргумента , массив верхнего уровня переписывается указателю, поэтому char *[], по сути, char**. Однако это относится только к параметрам функции .

char*[] (как в вашем втором случае) может преобразовать в char**, но результаты конверсии (как с любым преобразованием) является Rvalue, и не могут быть использованы для инициализации неконстантных ссылок. Зачем вам ссылка? Если изменить указатель, изменения char** аргумента main не определено поведение (формально, в C, по крайней мере — я не проверил, если C++ является более либеральным здесь). И, конечно же, вы не можете изменить постоянный адрес массива. И если вы не хотите, чтобы изменил его, зачем использовать ссылку?

+0

Я принял ответ реми за стандартную цитату. Спасибо! +1 – Felics

0

Тип temp в

char* temp[] = { "", "", nullptr };

является не символ * [], это

char*[3]

Последнее не может быть неявно преобразовано в `char ** '.

В main, тип argv является несвязанным char* массив, который эквивалентен char**

Признаюсь, это сбивает с толку :)

Смежные вопросы