В стандарте c ++, в [basic.lval] /11.6 сказано:
Если программа пытается получить доступ к сохраненному значению объекта через glvalue другого, чем один из следующих типов, поведение не определено: [...]
- агрегатный или объединенный тип, который включает в себя один из вышеупомянутых типов среди своих элементов или нестатических элементов данных (включая, рекурсивно, элемент или нестатический элемент данных субагрегата или содержанного объединения), [...]
Это предложение является частью строгого псевдонима .
Может ли это позволить нам получить доступ к неактивному члену несуществующего союза? Как в:
struct A{
int id :1;
int value :32;
};
struct Id{
int id :1;
};
union X{
A a;
Id id_;
};
void test(){
A a;
auto id = reinterpret_cast<X&>(a).id_; //UB or not?
}
Примечание: приведите объяснение того, что я не понимаю в стандарте, и почему приведенный выше пример может быть полезен.
Интересно, что может быть [basic.lval] /11.6 полезным.
[class.mfct.non-static] / 2 запрещает нам вызывать функцию-член объединения или агрегата «castted to»:
Если нестатическая функция-член класса X вызывается для объекта, который не относится к типу X или к типу, производному от X, поведение не определено.
Учитывая, что статический доступ к элементу данных или функция статического члена могут напрямую выполняться с использованием квалифицированного имени (a_class::a_static_member
),
единственный вариант использования [basic.lval] /11.6, может состоять в доступе к члену объединения "castted to". Я думал об использовании этого последнего стандартного правила для реализации «оптимизированного варианта». Этот вариант может содержать либо объект класса A, либо объект класса B, два из которых начинаются с битового поля размера 1, обозначающего тип:
class A{
unsigned type_id_ :1;
int value :31;
public:
A():type_id_{0}{}
void bar{};
void baz{};
};
class B{
unsigned type_id_ :1;
int value :31;
public:
B():type_id_{1}{}
int value() const;
void value(int);
void bar{};
void baz{};
};
struct type_id_t{
unsigned type_id_ :1;
};
struct AB_variant{
union {
A a;
B b;
type_id_t id;};
//[...]
static void foo(AB_variant& x){
if (x.id.type_id_==0){
reinterpret_cast<A&>(x).bar();
reinterpret_cast<A&>(x).baz();
}
else if (x.id.type_id_==1){
reinterpret_cast<B&>(x).bar();
reinterpret_cast<B&>(x).baz();
}
}
};
Вызов AB_variant::foo
не вызывает неопределенное поведение , пока его аргумент ссылается на объект типа AB_variant
благодаря правилу взаимозаменяемость указателя [basic.compound] / 4 . Доступ к неактивному члену объединения type_id_
разрешен, поскольку id
относится к общей начальной последовательности из A
, B
и type_id_t
[class.mem] / 25
Но что произойдет, если я попытаюсь вызвать его с полным объектом типа A
?
A a{};
AB_variant::foo(reinterpret_cast<AB_variant&>(a));
Проблема в том, что я пытаюсь получить доступ к неактивному члену объединения, которого не существует.
Два соответствующих стандартных абзаца: [class.mem] / 25 :
В объединении стандартной компоновки с активным членом типа структуры T1 разрешено читать нестатический член данных m другого члена объединения типа структуры T2, если m является частью общей начальной последовательности T1 и Т2; поведение такое, как если бы соответствующий член T1 был назначен.
И [class.union] / 1 :
В объединении нестатический элемент данных активен, если его имя относится к объекту , время жизни которого началось и не закончилось.
Q3 : Означает ли выражение «его имя» означает, что «объект» на самом деле является объектом, построенным в живом союзе? Или это может относиться к объекту a
из-за [basic.lval] /11.6.