Я пытаюсь определить, вызывает ли следующий код неопределенное поведение:
#include <iostream>
class A;
void f(A& f)
{
char* x = reinterpret_cast<char*>(&f);
for (int i = 0; i < 5; ++i)
std::cout << x[i];
}
int main(int argc, char** argue)
{
A* a = reinterpret_cast<A*>(new char[5])
f(*a);
}
Насколько я понимаю, reinterpret_cast
s до и от char*
совместимы, потому что стандарт разрешает наложение с указателями char
и unsigned char
(выделено мной):
Если программа пытается получить доступ к сохраненному значению объекта через lvalue, отличное от одного из следующих типов, поведение не определено:
- динамический тип объекта,
- cv-квалифицированная версия динамического типа объекта,
- тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта,
- тип, который является типом со знаком или без знака, соответствующим cv-квалифицированной версии динамического типа объекта,
- тип агрегата или объединения, который включает в себя один из вышеупомянутых типов среди своих членов (включая, рекурсивно, член субагрегата или содержащегося объединения),
- тип, который является (возможно, cv-квалифицированным) типом базового класса динамического типа объекта,
- типа
char
илиunsigned char
.
Однако я не уверен, вызывает ли f(*a)
неопределенное поведение, создавая A&
ссылку на недопустимый указатель. Решающим фактором, по-видимому, является то, что означает попытки использовать словоблудие в контексте стандарта C ++.
Моя интуиция такова, что это не составляет доступ, поскольку для доступа потребуется определить A
(он объявлен, но не определен в этом примере). К сожалению, я не могу найти конкретное определение доступа в стандарте C ++:
f(*a)
вызывает неопределенное поведение? Что представляет собой доступ в стандарте C ++?
Я понимаю, что, независимо от ответа, полагаться на такое поведение в производственном коде - плохая идея. Я задаю этот вопрос в первую очередь из-за желания улучшить свое понимание языка.
[Edit] @SergeyA процитировал этот раздел стандарта. Я включил его сюда для удобства (выделено мной):
5.3.1 / 1 [expr.unary.op]
Унарный оператор
*
выполняет косвенное обращение: выражение, к которому он применяется, должно быть указателем на тип объекта или указателем на тип функции, а результатом является lvalue, относящееся к объекту или функции, на которые указывает выражение. Если тип выражения - «указатель наT
», тип результата - «T
». [Примечание: косвенное обращение через указатель на неполный тип (кроме cv void) допустимо. Полученное таким образом lvalue можно использовать ограниченными способами (например, для инициализации ссылки); это lvalue нельзя преобразовывать в prvalue, см. 4.1. - конец примечания]
Прослеживая ссылку на 4.1, мы находим:
4,1 / 1 [conv.lval]
Значение glvalue (3.10) нефункционального типа
T
, не являющегося массивом, может быть преобразовано в prvalue. ЕслиT
- неполный тип, программа, которая требует этого преобразования, плохо сформирована. Если T - неклассовый тип, тип prvalue - это cv-неквалифицированная версияT
. В противном случае тип prvalue -T
.Когда преобразование lvalue-to-rvalue применяется к выражению
e
, либо:
e
потенциально не оценивается, или- оценка
e
приводит к оценке членаex
множества потенциальных результатовe
, аex
называет переменнуюx
, которая не используетсяex
(3.2)значение, содержащееся в указанном объекте, недоступно.
Я думаю, что наш ответ заключается в том, удовлетворяет ли *a
второму пункту. У меня проблемы с анализом этого условия, поэтому я не уверен.
A
? - person curiousguy   schedule 07.04.2016