Самый неприятный разбор с доступом к массиву

Просматривая какой-то код C++03, я обнаружил пример самого неприятного синтаксического анализа, который меня смутил:

#include <sstream>
#include <string>

int main(int, char** argv)
{
    std::stringstream ss(std::string(argv[0]));
}

живой пример на wandbox

В приведенном выше фрагменте ss — это объявление функции, которая принимает std::string* и возвращает std::stringstream.

Как std::string(argv[0]) интерпретируется как std::string*?

Интуитивно я подумал, что argv[0] однозначно является доступом к argv.


person Vittorio Romeo    schedule 12.12.2017    source источник
comment
Это не удалось скомпилировать для меня с Visual Studio 2015, потому что argv[0], по-видимому, представляет собой массив размера 0 типа argv. Я могу получить тот же результат, что и вы, если использую argv[1]. Интересный вопрос.   -  person François Andrieux    schedule 12.12.2017
comment
Я думаю, что это эквивалентно std::stringstream ss(std::string argv[]);, что само по себе эквивалентно std::stringstream ss(std::string * argv);.   -  person François Andrieux    schedule 12.12.2017
comment
Я думаю, что это было единственное правило, если оно выглядит как декларация, то так оно и есть. так, как @FrançoisAndrieux написал ваш экземпляр из std::string, это не экземпляр, а объявление для массива std:strings   -  person ExOfDe    schedule 12.12.2017
comment
CppCon 2017: Луи Брэнди, «Любопытно повторяющиеся ошибки C++ в Facebook» Ссылки указаны правильно временной код. Мне потребовалось время, чтобы вспомнить, в каком видео я узнал об этом, но оно идеально подходит к тому, с чем вы только что столкнулись.   -  person ExOfDe    schedule 13.12.2017


Ответы (2)


Причина в том, что в контексте объявления функции компилятор будет интерпретировать std::string(argv[0]) как std::string argv[0], то есть объявление массива нулевого размера в качестве параметра функции названного argv ( перекрывает argv из main, так как это другая область действия), что затем эквивалентно указателю с помощью распада массива в указатель.

Следовательно, std::stringstream ss(std::string(argv[0])); означает то же, что и std::stringstream ss(std::string* argv);

Редактировать: поскольку в комментариях было правильно аннотировано, объявления массивов нулевого размера недействительны в C++, что делает программу неправильной. При компиляции этого кода с -pedantic флагами (GCC и clang) будут выдаваться предупреждения. Visual Studio даже выдает ошибку компиляции. Однако для любого другого индекса массива, отличного от 0, приведенная выше аргументация остается в силе.

person Jodocus    schedule 12.12.2017
comment
так что самый неприятный синтаксический анализ является еще более злым, чем я всегда думал... это не только то, что он выглядит как объявление функции, это объявление функции, но также и то, что он выглядит как недопустимое объявление функции, это объявление функции... .omfg - person 463035818_is_not_a_number; 12.12.2017
comment
@tobi303: Взгляните на грамматику. Он определяет массивы примерно как type name_optional [ size_expression ], и нет специального грамматического правила для выражений, значение которых равно нулю. Это было бы очень сложно, поскольку вычисление выражений в C++ является полным по Тьюрингу. - person MSalters; 12.12.2017
comment
@ tobi303, однако, это не недопустимое объявление. В C++ при объявлении переменной имя переменной может заключаться в круглые скобки. Т.е. int(x) совпадает с int x. Вот почему std::string(argv[0]) можно рассматривать как std::string argv[0] в этом контексте. Просто удалите std::string, чтобы избежать MVP: std::stringstream ss(argv[0]); - person Remy Lebeau; 12.12.2017
comment
@RemyLebeau массивы размером 0 недействительны? - person 463035818_is_not_a_number; 12.12.2017
comment
@ tobi303 tobi303 да, но это не меняет того факта, что MVP по-прежнему будет анализировать его как массив фиксированного размера (даже если размер недопустим), а массив фиксированного размера распадается на указатель. - person Remy Lebeau; 12.12.2017
comment
@RemyLebeau да, понял. Просто так получилось, что void foo(double x[0]){} в порядке, из-за еще одной причуды С++ - person 463035818_is_not_a_number; 12.12.2017

Я считаю, что это следует из принципа «синтаксис объявления похож на синтаксис выражения» и того факта, что параметры «массива» являются указателями.

Следующие объявления массива эквивалентны:

int x[1];
int (x)[1];
int (x[1]);

более или менее, потому что x[a], (x)[a] и (x[a]) являются эквивалентными выражениями.

Таким образом,

std::stringstream ss(std::string(argv[0]))

                 <=>

std::stringstream ss(std::string argv[0])

                 <=>

std::stringstream ss(std::string* argv)
person molbdnilo    schedule 12.12.2017