Я разрабатываю код моделирования, полностью написанный на C++, где мы используем двойную точность для всех вычислений. Теперь я хочу, чтобы пользователь мог настроить его на использование одинарной точности. Проблема в том, что все наши матрицы определены как двойные, поэтому мне интересно, могу ли я иметь общий указатель и указывать тип во время выполнения. Например, предположим, что наш код выглядит так:
double *mat = nullptr;
mat = new double[some_size];
// Some operations...
delete[] mat;
Нет, я хочу использовать ту же переменную и не вводить новую для одинарной точности, поскольку это потребует множества изменений в коде. Я попробовал указатель void
, но проблема в том, что я не могу использовать индексы для доступа к элементам матрицы:
void *ptr = nullptr;
ptr = new complex<double>[10];
for (auto i = 0; i < 10; ++i) {
ptr[i].real(1.33445);
ptr[i].imag(443.223);
}
delete[] ptr;
В этом случае я получаю следующую ошибку:
error: subscript of pointer to incomplete type 'void'
Также я получаю предупреждение о том, что указатель void
нельзя удалить.
Теперь мой вопрос:
Есть ли какой-либо возможный способ (похожий на указатель void
) получить общий указатель, а затем правильно привести его к определенному типу во время выполнения?
Это похоже на работу профсоюза, но я предполагаю, что это также будет означать множество изменений. Но повсеместное размещение указателей сделает код хрупким и трудным для поддержки.
Использование шаблонов для типа данных является очевидным решением.
Не отмечайте C для вопросов C++.
Шаблоны C++ обсуждаются в любой хорошей книге и/или учебном пособии по C++. Как такие символы, как std::vector
, std::list
и т. д., работают с разными типами? Даже глядя на свой код, вы видите complex<double>
-- как это работает, где вы указываете, что complex
использует double
? Шаблоны.
@EricPostpischil Вопрос справедлив и для языка C.
@Dan Вопрос справедлив и для языка C. В коде вашего вопроса используются new
и delete
, поэтому это не C
. Во-вторых, в C++ есть способы решения этой проблемы, которых нет у C
.
Если вам нужен ответ, применимый к вставляемому сюда языку, задайте отдельный вопрос и отметьте тегом «язык вставки здесь». Используйте более одного языкового тега только для вопросов, касающихся программ, написанных более чем на одном языке, или для вопросов о конкретной разнице между языками. Это соглашение для этого сайта (насколько я понимаю).
Короткий ответ: нет. Последствием сохранения непустого указателя (в первом примере double *
) в void *
является потеря всей информации о типе. Более того, чтобы использовать void *
так, как будто он указывает на что-то другое, необходимо преобразовать указатель обратно в правильный тип, например. double *dptr = (double *)ptr; *dptr = 42.0;
правильно в вашем первом случае, но float *fptr = (float *)ptr; *fptr = 42.0f;
(или преобразование в ЛЮБОЙ тип, кроме double *
и разыменования) дает НЕОПРЕДЕЛЕННОЕ поведение. По сути, это одно и то же как в C, так и в C++.
По моему мнению, было бы более выгодно шаблонизировать ваш матричный код, указав T
в качестве параметра шаблона, а затем разработать способ выбора T
во время выполнения и работы оттуда (существуют различные способы сделать это). Использование void *
и попытка преодолеть все эти препятствия, упомянутые другими (например, неопределенное поведение), — это проигрышная попытка.
@Dan: Даже если бы вопрос был действительным для C, это не значит, что ответы будут такими же. В ответах могут использоваться специфические особенности языка. И это проблема для Stack Overflow, потому что он здесь не только для вас. Он задуман как надежное хранилище вопросов и ответов для других людей в будущем. Кто-то будет искать Stack Overflow, используя тег C, и мы не хотим, чтобы его результаты поиска были забиты бесполезными ответами на C++. Политика в отношении тегов C и C++ заключается в том, чтобы не объединять их без каких-либо причин, таких как вопрос о взаимодействии или различиях между языками.
Ваши «некоторые операции» вашего кода изменяются, если ваш тип — double*
вместо complex<double>*
, поскольку double
не имеет .real
и .imag
. Программирование шаблонов может дать вам лишь определенный результат, но тогда различия в типах придется обрабатывать либо абстракцией типов данных, либо специализацией шаблонов.
подойдет ли что-нибудь по номеру std::variant<double, float>? (С++17....)
Спасибо всем, кто потратил свое драгоценное время на эту проблему. Решение, опубликованное @Chukwujiobi Canon, решило мою проблему. Итак, я создам указатель void, а затем приведу его к правильному типу всякий раз, когда мне нужно будет передать матрицу соответствующей функции в LAPACK для правильного типа.
void*
не предназначен для такого использования. Единственное хорошее применение void*
— это низкоуровневый код, где нам нужно передать адрес ячейки памяти.
Если вам необходимо получить доступ к объекту, расположенному в этом месте памяти, как при использовании оператора индекса, вы должны явно преобразовать это void*
в T*
, где T
— полный тип.
Это означает, что вы будете выполнять множество явных преобразований, что небезопасно, если вы конвертируете в неправильный тип. В любом случае вот один из возможных подходов:
void* ptr = nullptr;
ptr = new complex<double>[10];
for (auto i = 0; i < 10; ++i) {
static_cast<complex<double>*>(ptr)[i].real(1.33445);
static_cast<complex<double>*>(ptr)[i].imag(443.223);
}
delete[] static_cast<complex<double>*>(ptr);
Большое спасибо за вашу помощь! Этот код решил мою проблему. Мне нужно передать указатель void в функции LAPACK и BLAS, и с помощью вашего решения я могу привести указатель в соответствии с функцией.
А
volid*
это просто: void ничего не значит, поэтому его нельзя использовать для указания правильного типа. И если бы вы использовали указатель наsingle
для доступа к матрицеdouble
, вы бы просто получили мусор, поскольку все смещения были бы неверными.