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

Фон

Я понимаю, что вопрос довольно громоздкий, поэтому постараюсь объяснить ситуацию в меру своих возможностей.

Начиная с C++20, агрегатную инициализацию шаблонных классов стало довольно легко использовать благодаря неявному выведению аргументов шаблона класса. Однако в большинстве реализаций, использующих это преимущество, создаются переменные-члены с явными именами.

В настоящее время я работаю с процессором, у которого есть неиспользуемый в противном случае регистр, и создаю крошечную библиотеку для взаимодействия с ним и остальной частью процессора; в идеале пользователи должны иметь возможность произвольно определять абстрактное содержимое регистра (например, указатель, четыре индекса uint8 и т. д.) без необходимости использовать имя переменной, навязанное им библиотекой.

В идеале пользователю не нужно было бы создавать объект, наследуемый от базового класса регистров, и он мог бы использовать агрегатную инициализацию.

Проблема

Следующая реализация C++23 функциональна, но не идеальна.

/* Library */


template <class UserInterpretation>
struct Register : public UserInterpretation {
    void MoveToPhysicalRegister(void) {
        u32& contents = *__builtin_bit_cast(u32*, this);
        asm(...); // Processor-specific assembly to move to desired register
    }
    void MoveFromPhysicalRegister(void) {
        asm(...); // Processor-specific assembly to move from desired register
    }
};

Эта реализация работает нормально, когда конструктор по умолчанию вызывается перед любыми изменениями объекта посредством пользовательской интерпретации. С неявным выводом аргументов шаблона также работает построение объекта RegisterInterpretation на месте.

Однако попытка совокупной инициализации объекта регистра с использованием пользовательских переменных не удалась.

/* User */


struct RegisterInterpretation {
    u32 InitMemAddr : 24;
    u8  InitMode;
};

// Works
void UserFunctionDefaultConstructorFollowedByModifications(void) {
    Register<RegisterInterpretation> userRegister;
    userRegister.InitMemAddr = 0xFB9000;
    userRegister.InitMode = 3;
    userRegister.MoveToPhysicalRegister();
    userRegister.InitMode = 2;
    userRegister.MoveToPhysicalRegister();
}

// Works
void UserFunctionConstructionInPlace(void) {
    Register userRegister = {RegisterInterpretation{.InitMemAddr = 0xFB9000, .InitMode = 3}};
    userRegister.MoveToPhysicalRegister();
    userRegister.InitMode = 2;
    userRegister.MoveToPhysicalRegister();
user
}

// Fails
void UserFunctionAttemptedAggInitError(void) {
    Register<RegisterInterpretation> userRegister = {.InitMemAddr = 0xFB9000, .InitMode = 0}; // ERROR
    userRegister.MoveToPhysicalRegister();
    userRegister.InitMode = 2;
    userRegister.MoveToPhysicalRegister();
}

Сгенерированная ошибка

В сбойном экземпляре генерируются две ошибки одного типа.

Ошибка: указатель поля «InitMemAddr» не ссылается ни на одно поле типа «Регистр»
Ошибка: указатель поля «InitMode» не ссылается ни на одно поле типа «Регистр».

Обратите внимание, что один и тот же результат получается как с Clang, так и с GCC.

Вопросы

У меня есть два вопроса относительно наблюдаемого поведения:

  1. Объект регистра наследует свой параметр шаблона, так почему же поля параметра шаблона не распознаются как поля объекта регистра?
  2. Есть ли что-нибудь в стандарте C++, объясняющее эти результаты?

Примечание: эта структура странная. InitMode никогда не будет гарантированно находиться в той же 32-битной ячейке памяти, что и InitMemAddr. Для этого он должен быть u32 InitMode: 8;.

Swift - Friday Pie 04.06.2024 07:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Только прямые члены могут быть названы в списке назначенных инициализаторов.

[dcl.init.aggr]/(3.1), выделение мое:

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

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

Похожие вопросы

Почему «Размещение нового» для классов с виртуальными членами работает только тогда, когда буфер находится в локальном стеке?
Ошибка Flex «пропущен конец буфера» для очень специфических входных данных
Перепишите код объявления переменной C++ через AST
У меня проблема с помещением информации в файл типа json
Почему GCC пересылает аргументы этого векторного конструктора конструктору содержащегося типа, а Clang — нет?
Что определяет, будет ли двоичный файл, скомпилированный с помощью MinGW, иметь доступ к API Win32 или API POSIX?
Загрузка артефакта символов, созданного PublishSymbols@2, из графического интерфейса Azure DevOps невозможна
Почему Boost уделяет особое внимание потокобезопасности для отдельных объектов?
Ошибка установки Хоровода в Docker — PIP — C++17
Можно ли статически собрать opencv под Linux, не имея проблем с отсутствующими/несовместимыми библиотеками?