Преобразование из длинного в одноименное int не выполняется - PullRequest
1 голос
/ 19 сентября 2019

Я пытаюсь преобразовать массив long в массив одинаковых размеров ( godbolt ):

#include <cstdint>
#include <cstddef>
#include <cstring>

template<int> struct sized_int_ {};      
template<> struct sized_int_<8> { typedef int64_t type; };   
template<> struct sized_int_<4> { typedef int32_t type; };
typedef sized_int_<sizeof(long)>::type long_t;              

void funcA(int32_t* array, std::size_t s); // defined somewhere else
void funcA(int64_t* array, std::size_t s); // defined somewhere else

// technically illegal cast
void funcB(long* array, std::size_t s) {
    funcA(static_cast<long_t*>(array), s);
}

// memcpy
void funcC(long* array, std::size_t s) {
    long_t* tmp = new long_t[s];
    memcpy(tmp, array, s*sizeof(long_t));
    funcA(tmp, s);
    delete[] tmp;
}

int main() {
    long x[] = {2, 3};
    static_assert(sizeof(long_t)==sizeof(long), "Sizes don't match");
    funcB(x, 2);
    funcC(x, 2);
    return 0;
}

g ++ с радостью принимает код, но лязгает на MacOSне (error: static_cast from 'long *' to 'long_t *' (aka 'long long *') is not allowed).reinterpret_cast работает в обоих случаях, но также позволяет приводить от int32_t* к int64_t*.

Правильный способ выделения временного массива и копирования в него данных не оптимизируется.по крайней мере, лязг и gcc с -O2.

Как я могу безопасно привести long* к соответствующему intX_t*?

Ответы [ 2 ]

2 голосов
/ 19 сентября 2019

Как я могу безопасно разыграть long на соответствующий intX_t?

Вам не нужно разыгрывать.Преобразование неявное:

long x = 2;
long_t y = x;

Но вы можете использовать статическое приведение, если хотите быть явным:

long_t y = static_cast<long_t>(x);

Обман указателя подразумевает, что вы можете захотеть иметь дело собъект на месте.Это, конечно, требует, чтобы типы имели одинаковое представление, что является довольно разумным предположением, хотя и не гарантируется.Но даже предположение, технически недостаточное для того, чтобы косвенность через reinterpret_cast было хорошо определено в соответствии со стандартом.

Технически можно использовать хранилище, создав объект нужного типа:

long_t temp = x;
long_t* reused_x = new(&x) long_t(temp);

После повторного использования вы можете конвертировать указатель в x на лету, если вы не можете сохранить тот, который был возвращен путем размещения нового путем отмывания.Обратите внимание, что отмывание недостаточно без нового размещения.

long_t* converted = std::launder(reinterpret_cast<long_t*>(&x));

То же самое можно сделать в цикле с массивами:

template<class T, class F> // types To and From
T* reuse_array(F* first, F* last, T* d_first) {
    for (F* ptr = first; ptr != last; ++d_first, (void) ++ptr) {
        T value = *ptr;
       ::new (ptr) T(value);
    }
   return std::launder(reinterpret_cast<T*>(first));
}

Это можно сделать даже со структурами, использующими std::memcpy пока они тривиально копируемы и разрушаемы.Достаточно хороший оптимизатор должен исключить копии .Обратите внимание, что время жизни x закончилось и больше не может использоваться.

Существует предложение о введении std::bless в язык, который должен устранить необходимость явного создания объектав подобных случаях.

0 голосов
/ 20 сентября 2019

Технически говоря, вам вообще не нужно ничего приводить, и если нет необходимости в функции-обертке, лучше шаблонировать ее, так как один из intX_t является псевдонимом одного из целочисленных типов, и перегрузка функций работает из-занеявное приведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...