Есть хороший доклад Джейсона Тернера и Бена Дина из C ++ Now 2017 под названием constexpr All the things
, который также дает constexpr
векторную реализацию. Я баловался с этой идеей сам, в образовательных целях. Мой вектор constexpr был чист в том смысле, что возвращение к нему вернуло бы новый вектор с добавленным элементом.
Во время разговора я увидел, что реализация push_back
выглядит примерно так:
constexpr void push_back(T const& e) {
if(size_ >= Size)
throw std::range_error("can't use more than Size");
else {
storage_[size_++] = e;
}
}
Они брали элемент по значению и перемещали его, но я не думаю, что это источник моих проблем. Я хочу знать, как эту функцию можно использовать в контексте constexpr? Это не постоянная функция-член, она изменяет состояние. Я не думаю, что можно сделать что-то вроде
constexpr cv::vector<int> v1;
v1.push_back(42);
И если это невозможно, как мы могли бы использовать эту вещь в контексте constexpr и достичь цели задачи, используя этот вектор, а именно анализ JSON во время компиляции?
Вот моя версия, так что вы можете увидеть как мою новую версию, возвращающую вектор, так и версию из беседы. (Обратите внимание, что вопросы производительности, идеальной пересылки и т. Д. Не учитываются)
#include <cstdint>
#include <array>
#include <type_traits>
namespace cx {
template <typename T, std::size_t Size = 10>
struct vector {
using iterator = typename std::array<T, Size>::iterator;
using const_iterator = typename std::array<T, Size>::const_iterator;
constexpr vector(std::initializer_list<T> const& l) {
for(auto& t : l) {
if(size_++ < Size)
storage_[size_] = std::move(t);
else
break;
}
}
constexpr vector(vector const& o, T const& t) {
storage_ = o.storage_;
size_ = o.size_;
storage_[size_++] = t;
}
constexpr auto begin() const { return storage_.begin(); }
constexpr auto end() const { return storage_.begin() + size_; }
constexpr auto size() const { return size_; }
constexpr void push_back(T const& e) {
if(size_ >= Size)
throw std::range_error("can't use more than Size");
else {
storage_[size_++] = e;
}
}
std::array<T, Size> storage_{};
std::size_t size_{};
};
}
template <typename T>
constexpr auto make_vector(std::initializer_list<T> const& l) {
return cx::vector<int>{l};
}
template <typename T>
constexpr auto push_back(cx::vector<T> const& o, T const& t) {
return cx::vector<int>{o, t};
}
int main() {
constexpr auto v1 = make_vector({1, 2, 3});
static_assert(v1.size() == 3);
constexpr auto v2 = push_back(v1, 4);
static_assert(v2.size() == 4);
static_assert(std::is_same_v<decltype(v1), decltype(v2)>);
// v1.push_back(4); fails on a constexpr context
}
Итак, это заставило меня понять, что, вероятно, что-то глубокое, что я не знаю о constexpr. Итак, повторяем вопрос; как такой вектор constexpr мог предложить мутирующий push_back
в контексте constexpr? Похоже, это не работает в контексте constexpr прямо сейчас. Если push_back
в контексте constexpr не предназначен для начала, как вы можете назвать его вектором constexpr и использовать его для анализа JSON во время компиляции?