2

Может быть кто-нибудь сталкивался с данной проблемой в MS VS 2015 и может предоставить ссылку на соответствующий раздел документации на сайте Microsoft?

Следующая программа на C++/CLI успешно компилируется

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
    array<String ^> ^a = { "one", "two", "three" };

    for each (auto s in a) Console::Write("{0} ", s);
    Console::WriteLine();

    return 0;
}

Обратите внимание на использование слова auto в предложении с for each.

Однако если использовать двухмерный массив, как показано в следующей программе

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
    array<String ^, 2> ^a =
    {
        { "one", "two" },
        { "three", "four" }
    };

    for each (auto s in a) Console::Write("{0} ", s);
    Console::WriteLine();

    return 0;
}

то компилятор уже не пропускает такой код и выдает сообщение об ошибке

Ошибка C3537 нельзя привести к типу, содержащему "auto"
Ошибка C2440 static_cast: невозможно преобразовать "System::String ^" в "int"

Ежели заменить слово auto на String ^, то программа будет успешно компилироваться.

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
    array<String ^, 2> ^a =
    {
        { "one", "two" },
        { "three", "four" }
    };

    for each (String ^s in a) Console::Write("{0} ", s);
    Console::WriteLine();

    return 0;
}

Хотелось бы посмотреть на описание данного ограничения в первоисточнике, то есть в документации на сайте Microsoft.

1 Answers1

2

Я просмотрел раздел 16.2.1 стандарта, и ничего, отличающего случай одномерных от случая многомерных массивов, не нашёл.

В стандарте упоминается следующее раскрытие цикла:

for each (T d in <collection-expr>) statement

раскрывается в

<enumeration-type> e = <collection-expr>.GetEnumerator(); 
while(e.MoveNext())
{ // в стандарте эта скобка пропущена, очевидная опечатка
    T d = safe_cast<T>(e.Current); 
    statement
} 

Видимо, для нашего случая T == auto и компиляция safe_cast<T> проваливается. Таким образом, здесь вопрос в порядке подстановки auto и раскрытия. И поскольку раскрытие цикла происходит чисто синтаксически (посмотрите на определение collection pattern!), то скорее всего, оно выполняется до семантического анализа и определения смысла auto.

Тем не менее, случай одномерного массива работает. Исходя из того, что эти случаи не различимы согласно стандарту, вы наткнулись на баг: одномерный случай, очевидно, обрабатывается специальным образом.


Разница между одномерным и многомерным массивами заключается ещё и в том, что первый имплементирует как IEnumerable<String^>, так и необобщённый IEnumerable, а второй — только необобщённый вариант. Но тест кастомного класса, имплементирующего лишь IEnumerable, проходит без ошибок.


P.S.: Если у вас есть возможность слезть с C++/CLI и перелезть на C#, поверьте, это стоит того.

VladD
  • 206,799