Использование преобразования указателя для хранения/приведения значений: нарушаю ли я строгое правило псевдонимов?

Вопрос относится к этому сообщению.

Некоторые авторитетные пользователи заявили, что следующий код нарушает строгие правила псевдонимов.

#include <boost/static_assert.hpp>

template <typename T>
struct MyType {

    private:
    T data;

    public:
    template <typename U>
    operator U () {
        BOOST_STATIC_ASSERT_MSG(sizeof(U) == sizeof(T),"Trying to convert to data type of different size");
        return *((U*) &data);
    }

    template <typename U>
    NeonVectorType<T>& operator =(const U& in) {
        BOOST_STATIC_ASSERT_MSG(sizeof(U) == sizeof(T),"Trying to copy from data type of different size");
        data = *((T*) &in);
        return *this;
    }
}

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

Примечание: я не знаю, насколько это важно, но мой компилятор (gcc 4.9) не выдает предупреждений.


person Antonio    schedule 25.03.2015    source источник
comment
Я подозреваю, что вы путаете строгие правила псевдонима с псевдонимом указателя, который является другой формой псевдонима.   -  person molbdnilo    schedule 25.03.2015


Ответы (2)


*((U*) &data) будет нарушать строгое использование псевдонимов, если это reinterpret_cast, а типу U не разрешено использовать псевдоним для типа T. Разрешенные типы отображаются в этом списке.

Правило относится к как к чтению, так и к записи.

Вот хорошая статья, объясняющая некоторые обоснование правил.

Как было отмечено в основной ветке строгого алиасинга, вы можете использовать memcpy в качестве обходного пути, например:

U u;
memcpy( &u, &data, sizeof u );
return u;

а в другой функции

memcpy( &data, &in, sizeof data );

Обратите внимание, что необработанные байтовые копии типов классов имеют некоторые ограничения (я думаю, что классы должны быть POD, и вам лучше убедиться, что они имеют одинаковую структуру).

person M.M    schedule 25.03.2015
comment
Если типы несовместимы (U не может использовать псевдоним T), почему компилятор не выдает предупреждение? Трудно ли получить эту информацию для компилятора? - person Antonio; 25.03.2015
comment
Это не очень хорошо для обнаружения этого нарушения. Как обсуждалось на страницах, на которые я ссылался, проход псевдонимов может происходить спустя долгое время после того, как код станет распознаваемым. - person M.M; 25.03.2015

Однако я никогда не использую указатель для записи данных [...]

Язык стандарта более общий; [basic.life]/7 содержит:

указатель, указывающий на исходный объект, ссылка, указывающая на исходный объект, или имя исходного объекта [...]

В вашем operator= вы используете lvalue типа T для записи в data, а в operator U вы используете указатель типа U для его чтения; где U и T не связаны между собой и не являются типами символов, это UB.

Просто используйте memcpy. Это гарантированно работает и эффективно (попробуйте)!

person ecatmur    schedule 25.03.2015
comment
Действительно, memcpy оптимизирован и генерирует точно такой же ассемблерный код! - person Antonio; 25.03.2015