Как объявить массив с авто - программирование
Подтвердить что ты не робот

Как объявить массив с авто

Я играл с auto, и я заметил, что для большинства случаев вы можете заменить определение переменной на auto, а затем назначить тип.

В следующем коде w и x эквивалентны (по умолчанию инициализированы int, но не позволяют получить потенциальные копии). Есть ли способ объявить z таким, что он имеет тот же тип, что и y?

int w{};
auto x = int{};
int y[5];
auto z = int[5];
4b9b3361

Ответ 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);

Ответ 2

Не совсем то же самое, но вы можете использовать array:

auto z = std::array<int, 5>();

Ответ 3

decltype работает с g++ 4.9.0 20130601 для этого:

 
#include <iostream>
#include <algorithm>

static std::ostream& logger = std::clog;

class A {
    static int _counter;
    int _id;
  public:
    A() : _id(++_counter) {
        logger << "\tA #" << _id << " c'tored\n";
    } 

    ~A() {
        //logger << "\tA #" << _id << " d'tor\n";
    } 

    inline int id() const{
        return _id;
    }
};
int A::_counter(0); 

std::ostream& operator<<(std::ostream& os, const A& a) {
    return os << a.id();
}

int main() {

    auto dump = [](const A& a){ logger << a << " ";};

    logger << "x init\n";
    A x[5]; 
    logger << "x contains: "; std::for_each(x, x+5, dump);

    logger << "\ndecltype(x) y init\n";
    decltype(x) y;
    logger << "y contains: ";  std::for_each(y, y+5, dump);
    logger << std::endl;

    return 0;
}

Вывод:  

x init
    A #1 c'tored
    A #2 c'tored
    A #3 c'tored
    A #4 c'tored
    A #5 c'tored
x contains: 1 2 3 4 5 
decltype(x) y init
    A #6 c'tored
    A #7 c'tored
    A #8 c'tored
    A #9 c'tored
    A #10 c'tored
y contains: 6 7 8 9 10