В стандарте 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 запрещает нам вызывать функцию-член объединения или агрегата, "приведенного к":
Если нестатическая функция-член класса X вызывается для объекта, который не относится к типу X или к типу, производному от X, поведение не определено.
Учитывая, что доступ к статическим элементам данных или статическая функция-член может выполняться напрямую с использованием квалифицированного имени (a_class::a_static_member
), единственным полезным случаем использования [basic.lval] /11.6 может быть доступ член профсоюза "кастом". Я думал об использовании этого последнего стандартного правила для реализации «оптимизированного варианта». Этот вариант может содержать либо объект класса 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 и T2; поведение такое, как если бы был назначен член-корреспондент T1.
В объединении нестатический член данных активен, если его имя относится к объекту, время существования которого началось и не закончилось.
Q3. Означает ли выражение «его название», что «объект» на самом деле является объектом, созданным в рамках живого союза? Или он может ссылаться на объект a
из-за [basic.lval] /11.6.
A
, который на самом деле является указателем наint
. (на самом деле это похоже на опечатку и должно бытьreinterpret_cast<A*>(&j)
и даже не компилируется как есть) - person user7860670   schedule 05.11.2018