инициализация массива constexpr - PullRequest
1 голос
/ 05 ноября 2019

У меня есть определенная и инициализированная структура данных, подобная следующей:

#include <vector>
#include <array>

struct SomeStruct {
    std::vector<int> vec;
};

int main() {
    std::array<SomeStruct, 2> arr {
        SomeStruct {
            .vec = {
                1, 2
            }
        },
        SomeStruct {
            .vec = {
                3, 4, 5
            }
        }
    };
}

Это компилируется правильно, но, поскольку вся структура известна во время компиляции, я попытался сделать ее constexpr.

Простое объявление arr как constexpr в предыдущем примере приводит к ошибке:

main.cpp: In function ‘int main()’:
main.cpp:20:5: error: the type ‘const std::array’ of constexpr variable ‘arr’ is not literal
     };
     ^
In file included from main.cpp:2:0:
/usr/include/c++/7/array:94:12: note: ‘std::array’ is not literal because:
     struct array
            ^~~~~
/usr/include/c++/7/array:94:12: note:   ‘std::array’ has a non-trivial destructor

Я предполагаю, что это потому, что std::vector не имеет constexprконструктор / деструктор.

Затем я попытался использовать std::array с шаблоном для содержащей структуры:

#include <array>

template <int N>
struct SomeStruct {
    std::array<int, N> vec;
};

int main() {
    constexpr std::array<SomeStruct, 2> arr {
        SomeStruct<2> {
            .vec = {
                1, 2
            }
        },
        SomeStruct<3> {
            .vec = {
                3, 4, 5
            }
        }
    };
}

Это также приводит к ошибке:

main.cpp: In function ‘int main()’:
main.cpp:10:39: error: type/value mismatch at argument 1 in template parameter list for ‘template struct std::array’
     constexpr std::array<SomeStruct, 2> arr {
                                       ^
main.cpp:10:39: note:   expected a type, got ‘SomeStruct’
main.cpp:10:41: error: scalar object ‘arr’ requires one element in initializer
     constexpr std::array<SomeStruct, 2> arr {
                                         ^~~

Но я не могу дать SomeStruct параметр шаблона, потому что размеры могут отличаться.

Каков наилучший способ определения структуры данных constexpr, подобной этой?

Ответы [ 2 ]

3 голосов
/ 05 ноября 2019

Как сказано здесь

Поскольку std::array<T, N> является агрегатом, его можно инициализировать как constexpr, если и только если базовый тип T имеет конструктор constexpr(при представлении каждого предоставленного вами инициализатора).

Поскольку std::vector не имеет конструктора constexpr ( пока ), это не будет работать.

Таким образом, в pre-C ++ 20 это не будет работать с контейнерами STL динамического размера. Нет решения или быстрого решения. Не надейтесь.

Альтернатива - создать свой собственный класс constexpr fixed-max-size Vector. Например,

template <typename T, std::size_t N>
class Vector {
private:
   T values[N]{};
public:
   std::size_t size{ 0 };
   constexpr bool empty() const noexcept { return size == 0; }
   constexpr void clear() noexcept { size = 0; }

   constexpr T* begin() noexcept { return &values[0]; }
   constexpr T* end() noexcept { return &values[size]; }
   constexpr T const* cbegin() const noexcept { return &values[0]; }
   constexpr T const* cend() const noexcept { return &values[size]; }

   constexpr T& back() noexcept { return values[size - 1]; }

   constexpr T operator[] (int const loc) const noexcept { return values[loc]; }
   constexpr T& operator[] (int const loc) noexcept { return values[loc]; }

   constexpr void push_back(T const& value) noexcept { values[size++] = value; }

   constexpr void resize(int const newSize) noexcept {
       auto const diff = newSize - size;
       if (diff > 0) memset(end(), 0, diff * sizeof(T));
       size = newSize;
   }
};

Это одна из тех, что я использую иногда ... вам нужно добавить конструктор initializer_list.

edit: Быстрый тест ... кажется, что он компилируется.

#include <array>

template <typename T, std::size_t N>
class Vector {
private:
   T values[N]{};
public:
    std::size_t size{ 0 };
    constexpr Vector(std::initializer_list<T> il) noexcept
     : size(std::distance(std::cbegin(il), std::cend(il)))
     {
         std::size_t i = 0;
         for(auto it = std::cbegin(il); it != std::cend(il); ++it) {
             values[i++]=*it;
         }
     }
};

struct SomeStruct {
    Vector<int,5> vec;
};

int main() {
    [[maybe_unused]]constexpr std::array<SomeStruct, 2> arr {
        SomeStruct {
            .vec = {
                1, 2
            }
        },
        SomeStruct {
            .vec = {
                3, 4, 5
            }
        }
    };
}
2 голосов
/ 05 ноября 2019

Если вы храните строки отдельно, то у вас может быть массив пролетов. Вы должны поддерживать строки по крайней мере столько же, сколько родительский массив. В этом примере это достигается тем, что они являются переменными в одной и той же области видимости:

struct SomeStruct {
    std::span<int> vec;
};

std::array arr1 {1, 2};
std::array arr2 {3, 4, 5};
constexpr std::array arr {
    SomeStruct { .vec = arr1 },
    SomeStruct { .vec = arr2 },
};

Или не изменяемая версия с большим количеством constexpr:

struct SomeStruct {
    std::span<const int> vec;
};

constexpr std::array arr1 {1, 2};
constexpr std::array arr2 {3, 4, 5};
constexpr std::array arr  {
    SomeStruct { .vec = arr1  },
    SomeStruct { .vec = arr2  },
};
...