Ответ 1
TL; DR
template<typename T, int N> using raw_array = T[N];
auto &&z = raw_array<int,5>{};
Ваш пример auto z = int[5];
не является законным, чем auto z = int;
, просто потому, что тип не является допустимым инициализатором. Вы можете написать: auto z = int{};
, потому что int{}
является допустимым инициализатором.
Как только вы это осознаете, следующая попытка:
auto z = int[5]{};
Обратите внимание, что ваш int y[5]
не имеет инициализатора. Если бы это было тогда, вы бы прыгнули прямо здесь.
К сожалению, это не работает ни по неясным причинам синтаксиса. Вместо этого вы должны найти законный способ назвать тип массива в инициализаторе. Например, имя typedef можно использовать в инициализаторе. Удобный многостраничный псевдоним типа шаблона устраняет обременительное требование нового typedef для каждого типа массива:
template<typename T, int N> using raw_array = T[N];
auto z = raw_array<int,5>{};
Кроме того: вы можете использовать псевдонимы типа шаблонов для исправления синтаксиса "наивысшего" языка С++, позволяющего вам называть любой составной тип упорядоченным, слева направо способом, используя это предложение.
К сожалению, из-за ошибки проектирования в C и С++, которая вызывает преобразования от матрица к указателю при падении шляпы, вычисленный тип переменной z
равен int*
скорее int[5]
. Результирующая переменная становится висящим указателем, когда временный массив уничтожается.
В С++ 14 вводится decltype(auto)
, который использует разные правила вывода типов, правильно выведя тип массива:
decltype(auto) z = raw_array<int,5>{};
Но теперь мы сталкиваемся с другой ошибкой дизайна с массивами; они не ведут себя как надлежащие объекты. Вы не можете назначать, копировать конструкцию, передавать по значению и т.д. С массивами. Вышеприведенный код похож на высказывание:
int g[5] = {};
int h[5] = g;
По всем правам это должно работать, но, к сожалению, встроенные массивы ведут себя странно на C и С++. В нашем случае особая проблема заключается в том, что массивам не разрешается иметь только какой-либо инициализатор; они строго ограничены использованием списков инициализаторов. Временной массив, инициализированный списком инициализаторов, сам по себе не является списком инициализаторов.
Ответ 1:
В этот момент Йоханнес Шауб делает превосходное предложение о том, что мы можем использовать временное продление жизни.
auto &&z = raw_array<int,5>{};
decltype(auto)
не требуется, потому что добавление &&
изменяет выведенный тип, поэтому предложение Иоганна Шауба работает на С++ 11. Это также позволяет избежать ограничения инициализаторов массива, поскольку мы инициализируем ссылку вместо массива.
Если вы хотите, чтобы массив выводил свою длину из инициализатора, вы можете использовать неполный тип массива:
template<typename T> using unsized_raw_array = T[];
auto &&z = unsized_raw_array<int>{1, 2, 3};
Хотя приведенное выше делает то, что вы хотите, вы можете предпочесть избегать необработанных массивов полностью из-за того, что необработанные массивы не ведут себя как правильные объекты С++ и неясность их поведения и техник, которые были использованы выше.
Ответ 2:
Шаблон std::array
в С++ 11 действует как правильный объект, включая присвоение, является проходимым по значению и т.д., и просто ведет себя нормально и последовательно, когда встроенные массивы этого не делают.
auto z = std::array<int,5>{};
Однако с этим вы упускаете возможность иметь тип массива, вывести свою собственную длину из инициализатора. Вместо этого вы можете написать функцию шаблона make_array
, которая делает вывод. Здесь действительно простая версия, которую я не тестировал, и которая не делает то, что вам может понадобиться, например, убедитесь, что все аргументы одного типа или вы явно указываете тип.
template<typename... T>
std::array<typename std::common_type<T...>::type, sizeof...(T)>
make_array(T &&...t) {
return {std::forward<T>(t)...};
}
auto z = make_array(1,2,3,4,5);