#c #pointers #polymorphism #constexpr #compile-time
Вопрос:
Предположим, у нас есть какая-то шаблонная структура:
template<typename T>
struct S {};
Мы хотим создать массив std::некоторого объекта типа S. Но, поскольку S-это структура шаблона, нам нужно создать базовый класс для хранения этих объектов.
struct AbstractS {};
template<typename T>
struct S : AbstractS {};
Теперь предположим, что у нас есть функция constexpr, которая создает массив std::и возвращает его, а также функция constexpr для получения этого массива.
constexpr auto createArray() {
constexpr auto v1 = S<int>{};
constexpr auto v2 = S<double>{};
std::array<const AbstractS *, 2> values{ amp;v1, amp;v2 };
return values;
}
constexpr void getArray() {
constexpr auto values = createArray();
}
Этот код не компилируется, и я думаю, что это связано с тем, что адреса v1 и v2 не являются константами.
Позвольте мне привести вам конкретный пример того, что я пытаюсь сделать.
struct AbstractPolynomial {};
template<typename T, std::size_t Degree>
struct Polynomial : AbstractPolynomial {};
У меня есть структура, которая моделирует полиномиальную функцию, где T-тип значений коэффициентов полинома, а Степень-степень полинома.
template<std::size_t N>
constexpr auto createArray() {
std::array<AbstractPolynomial *, N> polynomials;
for (std::size_t i = 0; i < N; i ) {
if (i % 2 == 0) {
polynomials[i] = amp;Polynomials<T1>{5};
} else {
polynomials[i] = amp;Polynomials<T2>{2, 5};
}
}
return polynomials;
}
Предположим, что у этого полинома есть руководство по дедукции (я не реализовал его здесь). Я знаю, что вы не можете получить временный адрес и строки назначения неверны, но я ввел эту форму, потому что хочу, чтобы вы дали мне решение для этого сценария.
Можем ли мы проделать какие-нибудь трюки, чтобы создать подобный сценарий?
Комментарии:
1. вернуть а
std::tuple<S<int>, S<double>>
?2. Я хочу что-то более общее, потому что у нас может быть более 2 типов.
3. Какую ошибку компилятора вы получаете?
4. std::массив<…> не является постоянным выражением. @alfC
5. std::кортеж может содержать более 2 элементов…
Ответ №1:
Это решение, обратите внимание, что полиномы должны быть сгенерированы изначально с сохранением их типа во время компиляции. Более поздний выбор не использовать эту информацию по какой-либо причине (динамический доступ) остается за вами.
Обратите внимание, что структура данных является constexpr, но не обязательно является оценкой полинома. Я думаю, что это происходит потому, что мешает возможностям функции. virtual
constexpr
evaluate
Вы можете поиграть с решением здесь: https://godbolt.org/z/oMreTzoGP
#include<array>
#include<cassert>
#include<tuple>
#include<utility>
// Dynamic polymials (questionable use of virtual functions)
struct AbstractPolynomial {
virtual auto evaluate() const -> double = 0;
};
template<typename T>
struct Polynomial : AbstractPolynomial {
constexpr Polynomial(T t) : value_{t}{}
T value_;
auto evaluate() const -> double override;
};
// instantiate and define two child classes for illustration
template<> auto Polynomial<double >::evaluate() const -> double {return value_;}
template<> auto Polynomial<std::pair<double, double>>::evaluate() const -> double {return value_.first value_.second;}
// Metaprogramming in this block doesn't assume virtual functions, Polynomial can be a concrete class
// functional form (on index and constructor args) taken from OP example
constexpr auto makePoly(std::integral_constant<int, 0>){return Polynomial<double >{5.};}
constexpr auto makePoly(std::integral_constant<int, 1>){return Polynomial<std::pair<double, double>>({2., 5.});}
// Tuples (not arrays) are created here
template <std::size_t... I>
constexpr auto createTuple_aux(std::index_sequence<I...>){
// do different things for even/odd cases (again taken from OP example)
return std::make_tuple(makePoly(std::integral_constant<int, I % 2>{})...);
}
template <std::size_t N> constexpr auto createTuple(){return createTuple_aux(std::make_index_sequence<N>{});}
// create 10 non-polymorphic polynamials in a tuple (preserve type information)
constexpr auto polyTuple = createTuple<10>();
// create 10 polymorphic polynamials in an array via pointers (type information is kept in the virtual table in pointer elements)
constexpr auto polyArrayPtr = std::apply([](auto constamp;... e){return std::array<AbstractPolynomial const*, std::tuple_size<decltype(polyTuple)>{}>{amp;e...};}, polyTuple);
int main(){
// test non-polymorphic access
assert( std::get<0>(polyTuple).evaluate() == 5. );
assert( std::get<1>(polyTuple).evaluate() == 7. );
assert( std::get<2>(polyTuple).evaluate() == 5. );
// test polymorphic access, indiraction
constexpr auto check = polyArrayPtr.size();
assert( polyArrayPtr[0]->evaluate() == 5. );
assert( polyArrayPtr[1]->evaluate() == 7. );
assert( polyArrayPtr[2]->evaluate() == 5. );
}
Комментарии:
1. Спасибо, @alfC . Ваше решение блестящее, мне нужно изучить std::index_sequence, потому что я не знаю, как на самом деле работает, но спасибо, я постараюсь использовать это 🙂
2. @apopa index_sequence задокументирован в cppreference.com. Не стесняйтесь принять или озвучить ответ, который вам больше всего нравится.
3. Я не могу озвучить ваш ответ прямо сейчас, потому что мой аккаунт почти новый, и у меня всего 11 очков. Когда я наберу больше очков, я поддержу ваш ответ. Еще раз спасибо.
Ответ №2:
Я не специалист в constexpr
этом . Но интуиция подсказывает мне, что ваши элементы должны быть глобальными и, например, статичными для функции.
Так как это невозможно в constexpr
функции (компилятор говорит мне) Я выставляю их на улицу, и все это работает.
#include<array>
struct AbstractS {};
template<typename T>
struct S : AbstractS {};
namespace detail{
constexpr auto v1 = S<int>{};
constexpr auto v2 = S<double>{};
}
constexpr auto createArray() {
constexpr std::array<const AbstractS *, 2> values{ amp;detail::v1, amp;detail::v2 };
return values;
}
constexpr void getArray() {
constexpr auto values = createArray();
}
int main(){}
Комментарии:
1. Да, я знаю, что элементы могут быть глобальными, и программа компилируется, но мне не нужны глобальные переменные. Предположим, у нас есть сотни значений. @alfC
2. @apopa, 1) статические переменные являются глобальными переменными. 2) если они генерируются динамически (потому что их сотни), то они не могут быть constexpr. Без дополнительного контекста я не могу вам помочь. Не говоря уже о том, что хранение указателя базового класса на самом деле не является решением, потому что вам нужно где-то хранить точный тип экземпляра. Я думаю, что вы ищете
std::tuple<S<int> , S<double> , ... >
.3. Предположим, у вас есть такой сценарий: шаблон<std::size_t N> constexpr auto f() { std::массив<std::size_t N><int, N> a; для (int i = 0; i <int, N> @alfC
4. @apopa, Если ваши сгенерированные значения constexpr параметризованы/сгенерированы целым числом, вы можете попробовать
std::index_sequence
. Для этого требуется немного метапрограммирования, но я не понимаю, что вы пытаетесь сделать.5. @apopa, что бы ты ни пытался сделать. Сначала сделайте это без constexpr, и сделайте это правильно, чтобы показать, чего вы хотите достичь.