Использование std::unique_ptr с выровненным типом приводит к предупреждению компилятора

clang выдает предупреждение, которое я не понимаю в данном контексте. Кажется, что указатель выровнен по 8 байтам, но clang жалуются на то, что он выровнен по 4 байтам.

Чтобы это исправить, я могу привести его к тому же типу, что и указатель, что кажется излишним.

Что мне здесь не хватает? Что мне следует прочитать/понять и можно ли это исправить?

#include <memory>

typedef struct 
{
    int type;
} type_t __attribute__ ((aligned (8))); // 3rd party

void doNothing(type_t*) { }

int main() {
    auto up = std::unique_ptr<type_t>(nullptr);
    auto raw_ptr = up.get();
    static_assert(alignof(type_t*) == 8);
    static_assert(alignof(decltype(raw_ptr)) == 8);
    doNothing(raw_ptr); // compile error: passing 4-byte aligned argument to 8-byte aligned parameter
    doNothing(reinterpret_cast<type_t*>(raw_ptr)); // ok with cast
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
89
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не уверен, что обмануло clang.

Но раз уж вы еще и спросили, как это исправить, то вот решение:
Вместо использования стиля C typedef ... используйте стиль C++ без typedef и определите struct напрямую:

struct __attribute__ ((aligned (8))) type_t 
{
    int type;
};

Помимо решения проблемы предупреждения, этот стиль также считается лучшим стилем в C++.

Живая демонстрация.

к сожалению, это сторонняя библиотека, поэтому я не могу это изменить.

Eugene 29.06.2024 11:04
Ответ принят как подходящий

alignof(type_t*) == 8 говорит вам, что выравнивание типа указателя выровнено по 8 байтам (что обычно справедливо в 64-битной системе), а не на то, что оно указывает на что-то, выровненное по 8 байтам.

С вашей программой и Clang, alignof(type_t) == 8, но alignof(decltype(*raw_ptr)) == 4.

Есть две формы __attribute__((aligned(N))). Один в структуре:

struct type_t __attribute__((aligned(8))) { int type; };
struct type_t { int type; } __attribute__((aligned(8)));
typedef struct { int type; } __attribute__((aligned(8))) type_t;

И один по имени типа:

typedef int more_aligned_int __attribute__ ((aligned (8)));
typedef struct type_t type_t __attribute__ ((aligned (8)));
typedef struct { int type; } type_t __attribute__((aligned(8)));
typedef struct type_t { int type; } type_t __attribute__((aligned(8)));

Тот, что в структуре, физически изменяет макет структуры, чтобы он был выровнен по 8 байтам:

typedef struct { int type; } __attribute__((aligned(8))) type_t;
static_assert(sizeof(type_t) == 8);  // 4 padding bytes
static_assert(alignof(type_t) == 8);

Тот, что указан в typedef-name, меняет выравнивание только при доступе к нему через это имя:

typedef int more_aligned_int __attribute__ ((aligned (8)));
typedef struct type_t { int type; } type_t __attribute__((aligned(8)));
static_assert(sizeof(more_aligned_int) == 4);
static_assert(alignof(more_aligned_int) == 8);
static_assert(sizeof(type_t) == 4);  // no padding bytes
static_assert(alignof(type_t) == 8);
static_assert(sizeof(struct type_t) == 4);
static_assert(alignof(struct type_t) == 4);  // Not 8: The struct's alignment did not change

Когда вы передаете выровненную структуру в параметр шаблона, ничего не происходит, поскольку структура размещается по-другому, чтобы быть более выровненной.
Однако когда вы передаете в шаблоны имя typedef, выравнивание нарушается. std::vector<more_aligned_int> имеет точно такой же тип, что и std::vector<int> (атрибут aligned ортогонален системе типов, это свойство typedef).

Clang удаляет выравнивание из имени typedef, что приводит к alignof(std::unique_ptr<type_t>::value_type) == 4, поскольку это выравнивание структуры, а не имени typedef. Итак, .get() возвращает невыровненный указатель, а raw_ptr имеет тип ptr без выравнивания.

Если вы специально возвращаете его к имени typedef:

  type_t* raw_ptr = up.get();

Оно будет снова выровнено.


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

Атрибут находится в неправильном месте для применения к структуре:

typedef struct 
{
    int type;
} __attribute__ ((aligned (8))) type_t;

// Now: sizeof(type_t) == 8, so you can have arrays of type_t.

Сообщите об этом своему стороннему поставщику библиотеки.

Другие вопросы по теме

Есть ли причина, по которой сравнения 8-байтовых std::array создают разные сборки для char и std::byte?
Как распечатать неструктурные результаты функций constexpr во время компиляции с помощью clang++?
Инициализация статического поля шаблона класса разные результаты в GCC и Clang
Можно ли дважды разыменовать («**itOpt») необязательный итератор?
Есть ли способ узнать, был ли введен тип замыкания в контексте инициализации встроенной переменной с помощью libclang?
MSVC позволяет функциям void возвращать значение (предупреждение компилятора C4098), но clang этого не делает
Неожиданный результат с `std::views::transform`, вероятно, вызван "безымянным временным"
Почему clang связан с msvc в Windows?
Почему я получаю 256-битную арифметическую ошибку: «unsigned _BitInt с битами больше 128 не поддерживается» в C++23, Clang-18?
Почему Clang запрещает использование квалификаторов в анонимных битовых полях?