Сделать взаимозаменяемые типы классов только с помощью приведения указателя, без необходимости выделения каких-либо новых объектов?

ОБНОВЛЕНИЕ: я ценю предложения "не хочу этого, вместо этого хочу это". Они полезны, особенно когда предоставляются в контексте format-database-m">мотивирующий сценарий. Тем не менее... независимо от того, хорошо это или плохо, мне стало любопытно найти жесткое и быстрое "да, это можно сделать легально в C++11" против "нет, это невозможно сделать что-то подобное".


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

Не должно быть никакой информации о каком-либо конкретном псевдониме, который когда-либо закодирован в базовом объекте. Следовательно, я чувствую, что вы должны иметь возможность «обмануть» систему типов и просто позволить ей быть аннотацией ... проверенной во время компиляции, но в конечном итоге не имеющей отношения к приведению во время выполнения. Что-то в этом роде:

Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);

Под капотом фабричный метод фактически создает класс NodeBase, а затем использует аналогичный reinterpret_cast, чтобы вернуть его как Node<AccessorFoo>*.

Простой способ избежать этого — создать эти легковесные классы, которые оборачивают узлы и передаются по значению. Таким образом, вам не нужно приведение, только классы Accessor, которые берут дескриптор узла для переноса в свой конструктор:

AccessorFoo foo (NodeBase::createViaFactory());
AccessorBar bar (foo.getNode());

Но если мне не нужно платить за все это, я не хочу. Это может включать, например, создание специального типа средства доступа для каждого типа обернутого указателя (AccessorFooShared, AccessorFooUnique, AccessorFooWeak и т. д.). Использование псевдонимов этих типизированных указателей для одного идентификатора объекта на основе указателя является предпочтительным и обеспечивает хорошая ортогональность.

Итак, вернемся к исходному вопросу:

Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);

Кажется, что есть какой-то способ сделать это, который может быть уродливым, но не «нарушать правила». Согласно ISO14882:2011(e) 5.2.10-7:

Указатель объекта может быть явно преобразован в указатель объекта другого типа.70 Когда prvalue v типа «указатель на T1» преобразуется в тип «указатель на cv T2», результатом является static_cast(static_cast(v)) если и T1, и T2 являются типами стандартной компоновки (3.9) и требования выравнивания T2 не более строгие, чем требования T1, или если любой из типов недействителен. Преобразование значения prvalue типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 — типы объектов и требования выравнивания T2 не более строгие, чем требования T1) и обратно к исходному типу дает исходный значение указателя. Результат любого другого такого преобразования указателя не указан.

Детализация определения "стандартного макета class", находим:

  • не имеет нестатических элементов данных типа non-standard-layout-class (или массива таких типов) или ссылки, и
  • не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1), и
  • имеет одинаковый контроль доступа (пункт 11) для всех нестатических элементов данных, и
  • не имеет базовых классов нестандартной компоновки, и
  • либо не имеет нестатических элементов данных в наиболее производном классе и не более одного базового класса с нестатическими элементами данных, либо не имеет базовых классов с нестатическими элементами данных, и
  • не имеет базовых классов того же типа, что и первый нестатический член данных.

Похоже, работа с чем-то вроде этого немного свяжет мне руки без виртуальных методов в аксессорах или узле. Тем не менее, C++11, по-видимому, имеет std::is_standard_layout для проверки.

Можно ли это сделать безопасно? Кажется, работает в gcc-4.7, но я хотел бы быть уверен, что не вызываю неопределенное поведение.


person HostileFork says dont trust SE    schedule 27.06.2012    source источник
comment
Начальный синтаксис должен быть Node<AccessorFoo>& foo = *new Node<AccessorFoo>;. Я все равно не понимаю вопроса :)   -  person iammilind    schedule 27.06.2012
comment
@iammilind К сожалению, опечатка исправлена... спасибо! В принципе, это, вероятно, очень похоже на это вопрос, я просто лучше контролирую задействованные типы и могу переписать все с нуля. Поэтому мне интересно, какие варианты могут открыться для меня в этом конкретном сценарии.   -  person HostileFork says dont trust SE    schedule 27.06.2012
comment
Я хочу использовать псевдоним указателя объекта как другого типа с единственной целью добавления некоторых вспомогательных методов. Почему? Что дают вам функции-члены, чего не могут дать бесплатные функции? Если я не упустил что-то очевидное (что очень возможно), это звучит как немного ошибочного фанатизма ООП...   -  person ildjarn    schedule 13.07.2012
comment
@ildjarn Может быть? Я сетовал на то, что SO часто представляет собой «проклятый, если вы объясните» (Что со всеми подробностями, каков ваш фактический вопрос?) по сравнению с «проклятым, если вы не сделаете» объясните (Зачем вам это нужно?) среду. :( У меня есть окончательный класс, который имеет NC константных операций и N неконстантных операций, живет в файле с отображением памяти. Иногда я хотел бы расширить и ввести интерфейс этого класса в качестве подсказки, чтобы помочь людям. DOM является лучшей аналогией У меня есть, к сожалению   -  person HostileFork says dont trust SE    schedule 13.07.2012


Ответы (4)


Термин Accessor является бесполезным: вы ищете < em>Прокси.

Нет никаких причин для того, чтобы прокси не передавался по значению.

// Let us imagine that NodeBase is now called Node, since there is no inheritance

class AccessorFoo {
public:
    AccessorFoo(Node& n): node(n) {}

    int bar() const { return node->bar; }

private:
    std::reference_wrapper<Node> node;
};

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

person Matthieu M.    schedule 27.06.2012
comment
+1 Спасибо... хотя со страниц этого сайта это больше похоже на фасад , хотя на практике мне может понадобиться Decorator. Адаптер предоставляет другой интерфейс своему субъекту. Прокси предоставляет тот же интерфейс. Decorator предоставляет расширенный интерфейс (...) Facade определяет новый интерфейс, тогда как Adapter повторно использует старый интерфейс. Я хочу, чтобы объектно-реляционная модель была компромиссом, чтобы соответствовать моим желаниям (любым выбор будет), но просто не хочу активно незаконный C++. Отсюда вопрос. - person HostileFork says dont trust SE; 27.06.2012
comment
@HostileFork: имена всегда проблематичны. Facade обычно представляет собой совокупность интерфейсов (например, libclang — это C-Facade поверх библиотек Clang). Идея о том, что Proxy предоставляет тот же интерфейс, довольно ограничительна. Из Википедии Proxy предоставляет заполнитель для другого объекта для управления доступом, снижения стоимости и снижения сложности. Ваш вопрос очень похож на управление доступом, и в этом случае он естественно представить ограниченный интерфейс. - person Matthieu M.; 27.06.2012
comment
Вы говорите Нет никаких причин для того, чтобы прокси-сервер не передавался по значению, что противоречит моему представлению о том, что причина может быть. А именно: если вам нужна эффективная связь is-a, то если бы указатель на прокси был также указателем на узел, тогда вы можете использовать unique_ptr (shared_ptr, weak_ptr и т. д.) с прокси так же, как вы могли бы используйте его с прямым указателем узла. Это работает, если вы используете наследование C++ для реализации шаблона, но если ваш объект является реализацией элемента внешней базы данных, это не так. Я спрашиваю, можно ли исправить этот пробел в легальном С++. - person HostileFork says dont trust SE; 27.06.2012
comment
@HostileFork: Но Proxy не является узлом, поскольку он специально предназначен для ограничения доступного интерфейса. не так ли? - person Matthieu M.; 27.06.2012
comment
Извините, что я на самом деле не определил, предназначен ли он для ограничения или расширения интерфейса Node. Более важным моментом является то, что, учитывая понимание клиентом того, что он всегда может получить общий узел, любое ограничение будет заключаться только в том, уверены ли вы, что хотите сделать этот шаг, побуждая вас проверить интерфейс доступа. чтобы увидеть, будет ли более подходящей операция более высокого порядка. Если бы ограничение на самом деле было целью, я бы выполнял фактическое приведение внутри метода-оболочки, чтобы имитировать единственные производные классы, знающие об отношении is-a защищенного наследования. - person HostileFork says dont trust SE; 27.06.2012
comment
@HostileFork: в этом случае вам все равно не нужно наследование. Достаточно простого explicit operator Node& () { return *node; } (и его аналога const). - person Matthieu M.; 27.06.2012
comment
Да... если один содержит указатель узла, то, безусловно, можно извлечь содержащийся узел с помощью оператора приведения, не наследуя при этом от узла. (?) Может быть, я неправильно понимаю вашу точку зрения, но я не вижу, чтобы она применялась к этой, возможно, донкихотской и/или противоречивой цели, чтобы попытаться получить эти преимущества с помощью относительно нового интерфейса для объекта, переданного указателем. Обертывание его в новый класс значений — даже если он имеет операции приведения — не повлияет на это конкретное свойство. - person HostileFork says dont trust SE; 27.06.2012
comment
Похоже, что даже со стандартными типами макетов это запрещено строгими требованиями к псевдонимам (см. ответ от @PaulGroke). В таком случае единственное, что стандарт позволяет вам делать, это временно удерживать указатель как другой тип, а затем преобразовывать его обратно, но не использовать его в это время? (Возможно, более конкретно, не вызывать какие-либо неконстантные методы?) - person HostileFork says dont trust SE; 07.07.2012
comment
Я ценю время, которое вы потратили на свои ответы... и ваше замечание о том, что проникновение в темные области спецификации, вероятно, доставляет больше хлопот, чем оно того стоит на практике. Тем не менее, в интересах формального понимания, я думаю, что может быть полезно выяснить ответы на такого рода вопросы, в той мере, в какой они существуют. Смысл тега language-lawyer состоит в том, чтобы добиваться этого понимания, независимо от того, разумны ли мотивы или нет... ! - person HostileFork says dont trust SE; 10.07.2012
comment
Я последовал вашему совету (надеюсь/думаю?) и поработал над реализацией прокси-системы, которая, надеюсь, не создает каких-либо проблем с неопределенным поведением, но при этом обеспечивает поведение клиента, которое мне нужно. Вы можете сделать Accessor наследованием protected или public. У него есть шаблон, который наследуется от его собственной специализации частичного шаблона, чтобы реализовать изменяемый вариант как производную версию константного варианта, что улучшает несколько вещей. Это относительно коротко и странно, но вот суть (каламбур) - person HostileFork says dont trust SE; 14.07.2012
comment
Вы, наверное, устали от этого вопроса (и я вас не виню), но я решаю ответы на все свои непринятые вопросы. И если у вас есть что сказать о соответствующем обзоре кода: вот оно (на данный момент вознаграждено). - person HostileFork says dont trust SE; 03.05.2014
comment
Есть ли какая-то причина использовать reference_wrapper (указатель, который сам обернут сахарными средствами доступа) здесь, когда фактический элемент ссылки будет работать нормально? Мне кажется ненужным усложнением, если только я что-то не упускаю. - person underscore_d; 08.04.2016
comment
@underscore_d: выполнение заданий. Вы не можете присвоить новую ссылку существующей ссылке (т. е. переустановить ссылку), она просто присваивает указанное значение. - person Matthieu M.; 08.04.2016
comment
Ах, конечно, это то, чего мне не хватало. Я забыл об этом преимуществе reference_wrapper, так как мне никогда не приходилось его использовать, и я склонен забывать более мелкие детали. Спасибо! - person underscore_d; 08.04.2016

Я считаю, что строгие правила псевдонимов запрещают то, что вы пытаетесь сделать.

Чтобы уточнить: строгое сглаживание не имеет ничего общего с совместимостью макета, типами POD или чем-то еще. Это связано с оптимизацией. И с тем, что язык явно запрещает вам делать.

Этот документ довольно хорошо обобщает это: http://dbp-consulting.com/StrictAliasing.pdf

person Paul Groke    schedule 02.07.2012
comment
+1, так как я нашел несколько полезных тегов (например, каламбур) и точки перехода от строгого псевдонима. Но большинство ссылок со строгим псевдонимом, которые я нашел, похоже, говорят о типах с разным размером или макетом, и здесь различаются только методы без виртуальной диспетчеризации, поскольку все они построены как один и тот же тип. Кажется, что можно даже приводить от подписанных к неподписанным вариациям одного и того же типа в строгом псевдониме... поэтому я думаю, что этот вопрос отправки метода будет зависеть от чего-то вроде этого стандартного определения макета (если что-нибудь), или, может быть, какой-то другое зонтичное правило? - person HostileFork says dont trust SE; 03.07.2012
comment
Интересно, спасибо за обновление с PDF, я прочитал это и понял, о чем вы говорите. Хотя теперь мне интересно... если бы все методы класса были const, то есть элементы данных никогда не изменялись в течение жизни... было бы это законным? (И если нет, то почему?) - person HostileFork says dont trust SE; 06.07.2012
comment
@HostileFork: Этого не будет, потому что компилятору не нужно гарантировать, что, пока вы выполняете метод const для объекта, он не изменится у вас под ногами через другой тип. - person Matthieu M.; 07.07.2012
comment
@MatthieuM. В свете ответа jthill, существует ли такая вещь, как безопасное приведение к фундаментальному типу перед каждым доступом, который может обойти проблемы с псевдонимами, если строго соблюдать? - person HostileFork says dont trust SE; 09.07.2012
comment
@HostileFork: Честно? Я не знаю. И даже не вижу смысла. Стандарт С++ чрезвычайно сложен и сложен для навигации, и в данный момент у меня нет времени, чтобы попытаться проверить все, казалось бы, неработающие ответы, сгенерированные вашим вопросом. Поэтому я скажу только одно: чем ближе вы подходите к краям спецификаций, тем больше вероятность того, что вы столкнетесь с ошибкой компилятора. Итак, как инженер, я советую быть прагматичным и избегать игр разума с вашим компилятором; правы вы или нет, не имеет значения: когда вы получаете сбой в рабочем коде, проигрываете вы, а не авторы компилятора. - person Matthieu M.; 09.07.2012
comment
Я собираюсь наградить этот ответ наградой, поскольку он выявил этот строгий псевдоним, о котором я еще не знал ... который кажется принципиально важным для беспокойства как причина, по которой это может быть технически запрещено. Кажется, все еще существует серая область относительно того, достаточно ли безопасного приведения, которое вызывает упоминание базового типа, для обхода этого (см. jthill answer), хотя это, конечно, становится слишком хрупким для применения в большинстве разумных обстоятельств. - person HostileFork says dont trust SE; 10.07.2012

Если я правильно вас понял, у вас:

  • Класс NodeBase с сохранением состояния и настоящая рабочая лошадка системы;
  • набор типов Accessor без сохранения состояния, которые предоставляют интерфейс для NodeBase; и
  • класс Node<AccessorT>, обертывающий метод доступа, предположительно предоставляющий удобные функции.

Я предполагаю последний бит, потому что, если у вас нет типа-оболочки, который делает удобные вещи, то нет причин не делать типы Accessor вашими верхним уровнем, как вы предложили: передавать AccessorFoo и AccessorBar по значению. Тот факт, что они не являются одним и тем же объектом, совершенно спорен; если вы думаете о них как об указателях, то вы заметите, что &foo != &bar не более интересно, чем иметь NodeBase* p1 = new NodeBase; NodeBase* p2 = p1; и отметить, что, конечно же, &p1 != &p2.

Если вам действительно нужна оболочка Node<AccessorT> и вы хотите сделать ее стандартной компоновки, то я бы посоветовал вам использовать отсутствие состояния ваших типов Accessor в своих интересах. Если они являются просто контейнером функциональности без сохранения состояния (а так и должно быть; зачем еще вы могли бы свободно использовать их?), тогда вы можете сделать что-то вроде этого:

struct AccessorFoo {
    int getValue(NodeBase* n) { return n->getValueFoo(); }
};

struct AccessorBar {
    int getValue(NodeBase* n) { return n->getValueBar(); }
};

template <typename AccessorT>
class Node {
    NodeBase* m_node;

public:
    int getValue() {
        AccessorT accessor;
        return accessor.getValue(m_node);
    }
};

В этом случае вы можете добавить шаблонный оператор преобразования:

template <typename OtherT>
operator Node<OtherT>() {
    return Node<OtherT>(m_node);
}

И теперь у вас есть прямое преобразование значений между любым типом Node<AccessorT>, который вам нравится.

Если вы пойдете немного дальше, вы сделаете все методы Accessor типов статическими и получите признаки шаблона.


Между прочим, раздел стандарта C++, который вы процитировали, касается поведения reinterpret_cast<T*>(p) в случае, когда и исходный, и окончательный типы являются указателями на объекты стандартного макета, и в этом случае стандарт гарантирует, что вы получите тот же указатель, что и вы. от приведения к void*, а затем к окончательному типу. Вы по-прежнему не можете использовать объект как любой другой тип, кроме типа, для которого он был создан, без вызова неопределенного поведения.

person John Calsbeek    schedule 27.06.2012
comment
+1 Да, вы поняли суть, и методы доступа должны быть статическими ... хотя это будет сложно для авторов Accessor по сравнению с возможностью вызывать методы NodeBase, когда они наследуются от него. :-/ Я явно хочу, чтобы эти типы Node<X> не были отделены от экземпляра рабочей лошадки NodeBase, потому что я использую unique_ptr<Node<X>>, чтобы следовать принципу владения узлами. Чтобы продолжать использовать это, мне пришлось бы добавить еще один уровень отслеживания распределения кучи для этих Node<X> дескрипторов вместо передачи по значению. Так что меня больше всего интересует трюк со стандартной компоновкой. - person HostileFork says dont trust SE; 27.06.2012
comment
@HostileFork Почему бы не инвертировать это и не поместить unique_ptr внутри Node<X>? Если вам нужны другие виды интеллектуальных указателей, достаточно легко создать различные варианты Node<X>, особенно если это небольшая оболочка вокруг метода доступа. Кроме того, присмотревшись внимательно, я не думаю, что трюк со стандартным макетом - это то, что вы думаете. Цитируемый раздел посвящен reinterpret_cast разделению семантики с static_cast в случае типов стандартной компоновки. Вы по-прежнему не можете изменять типы без вызова неопределенного поведения. - person John Calsbeek; 27.06.2012
comment
Я не уловил этот момент... хм... на самом деле узлы создаются с помощью фабричного метода как NodeBase, а затем отбрасываются при возврате... будет ли это законным здесь или это ничего не меняет? Что касается создания легковесных классов, таких как UniqueNode<X>, которые отличаются от Node<X>... звучит возможно, много вещей возможно! Я просто пытаюсь вбить это в четкое разделение задач, которое не тратит время на ненужные комбинаторные взрывы классов и является максимально эффективным. (Я бы, наверное, использовал другой язык, если бы не нашел это мотивирующим.) :) - person HostileFork says dont trust SE; 27.06.2012
comment
Похоже, что даже со стандартными типами макетов это запрещено строгими требованиями к псевдонимам (см. ответ от @PaulGroke). В таком случае единственное, что стандарт позволяет вам делать, это временно удерживать указатель как другой тип, а затем преобразовывать его обратно, но не использовать его в это время? (Возможно, более конкретно, не вызывать какие-либо неконстантные методы?) - person HostileFork says dont trust SE; 07.07.2012
comment
Поработав некоторое время над этим вопросом, я думаю, что я собираюсь использовать: пример библиотеки для шаблона и тестовая программа на Gist. Foo в данном случае — это Node, а Wrapper — это то, что я, вероятно, назову NodeRef или что-то в этом роде. Это все еще кажется немного обходным путем для достижения эффекта по сравнению с каламбуром, но если это устраняет неопределенное поведение и клиентский код выглядит нормально, я думаю, все в порядке...! - person HostileFork says dont trust SE; 14.07.2012

struct myPOD {
   int data1;
   // ...
};

struct myPOD_extended1 : myPOD {
   int helper() { (*(myPOD*)this)->data1 = 6; };  // type myPOD referenced here
};
struct myPOD_extended2 : myPOD { 
   int helper() { data1 = 7; };                   // no syntactic ref to myPOD
};
struct myPOD_extended3 : myPOD {
   int helper() { (*(myPOD*)this)->data1 = 8; };  // type myPOD referenced here
};
void doit(myPOD *it)
{
    ((myPOD_extended1*)it)->helper();
    // ((myPOD_extended2*)it)->helper(); // uncomment -> program/compile undefined
    ((myPOD_extended3*)it)->helper();
}

int main(int,char**)
{
    myPOD a; a.data1=5; 
    doit(&a);

    std::cout<< a.data1 << '\n';
    return 0;
}

Я считаю, что это гарантированно работает во всех соответствующих компиляторах C++ и должно печатать 8. Раскомментируйте отмеченную строку, и все ставки сняты.

Оптимизатор может сократить поиск действительных ссылок с псевдонимами, проверяя список синтаксических типов, на которые фактически ссылается функция, по списку (в 3.10p10) синтаксических типов ссылок, которые требуются для получения правильных результатов, - и когда фактические («динамические ") известен тип объекта, этот список не включает доступ через ссылку на какой-либо производный тип. Таким образом, явное (и действительное) приведение вниз от this к myPOD* в helper()s помещает myPOD в список типов, на которые там синтаксически ссылаются, и оптимизатор должен рассматривать полученные ссылки как потенциальные допустимые ссылки на (другие имена, псевдонимы) объекта. a.

person jthill    schedule 07.07.2012
comment
Аргументы о псевдонимах, выдвинутые другими, похоже, предполагают, что компилятор может предположить, что псевдонимы как другие типы, кроме того, по которому был выделен класс (myPOD в этом случае), не существуют. Следовательно, data1 может быть изменено в одном из псевдонимов, а затем это изменение не будет отражено в другом. @MatthieuM предположил, что даже константность не защитит вас, поскольку один из псевдонимов может быть непостоянным. Можете ли вы назвать причину, по которой вы считаете, что это будет исключением из правил? - person HostileFork says dont trust SE; 08.07.2012
comment
Вы можете преобразовать structA* в (n даже совершенно не связанный) structB* и обратно, и компилятор должен учитывать эту возможность: преобразование указателя и его передача не означает, что преобразованное значение никогда не может быть использовано для изменения исходного объект. - person jthill; 08.07.2012
comment
Похоже, то, что вы говорите, противоречит строгому документу о псевдонимах, который предполагает, что вы можете сделать это только тогда, когда вы выполняете приведение типа между фактическим типом, с которым что-то было выделено (например, если вы объявили что-то как myPOD_extended1, а затем вы приводите его к myPOD и обратно). Это центральный аргумент вопроса, который я задаю, и это действительно звучит так, как будто строгое использование псевдонимов позволяет компилятору делать предположения об оптимизации, которые подорвали бы это в других случаях. :-/ - person HostileFork says dont trust SE; 08.07.2012
comment
Как только компилятор увидит doit(&a), нельзя предполагать, что a не будет доступен через этот указатель или любую копию. Восходящие преобразования в doit явно разрешены, см. 5.4p4, а функции helper() получают копию этого указателя восходящего преобразования (как this), поэтому компилятор не может предположить, что они не ссылаются на myPOD через эту копию. Я добавил безопасные приведения к вспомогательным функциям; Я думаю, что компилятору пришлось бы приложить все усилия, чтобы неправильно обработать этот случай, но я вижу, что раньше это было не совсем правильно (что в этом обсуждении означает «было неправильно»). Я считаю, что теперь это строго правильно. - person jthill; 09.07.2012
comment
Хорошо... так что, если вы говорите, что выполнение приведения к базовому типу до любых модификаций предотвращает сглаживание, то то, что я хочу сделать, будет законным...? Потому что мои производные типы никогда не добавляют членов. Я предполагаю, что хитрость заключается в том, чтобы убедиться, что все доступы к элементам данных проходят через то, что вы называете приведением безопасности (чтение и запись), вместо того, чтобы пытаться получить доступ напрямую без приведения, которое было бы псевдонимом и значит нелегально? - person HostileFork says dont trust SE; 09.07.2012
comment
тл; др: да. Длинный: я могу (теперь) видеть, что оптимизатор будет сокращать поиск допустимых ссылок с псевдонимами, проверяя список синтаксических типов, на которые фактически ссылается функция, по списку (в 3.10p10) синтаксических типов, для которых требуется получить правильные результаты - - и когда известен фактический (динамический) тип объекта, этот список не включает доступ через ссылку на какой-либо производный тип. Справедливо. Таким образом, явное понижение this помещает myPOD в список типов, на которые ссылаются helper(), поэтому, если оптимизатор не может доказать this != &a, он должен рассматривать их как (действительные) псевдонимы. - person jthill; 09.07.2012
comment
Этот ответ кажется мне неправильным. a не является myPOD_extended1 (или 2, или 3), поэтому идея приведения к указателю на этот тип выглядит неправильно; и я не понимаю, как цитируемые вами абзацы могли бы это как-то оправдать. - person Matthieu M.; 09.07.2012
comment
Если вы прочитаете 5.4p4, вы увидите, что первое применимое преобразование — это reinterpret_cast. 5.2.10p7 является применимым абзацем и включает в себя Указатель на объект может быть явно преобразован в указатель на объект другого типа.[...] Преобразование значения prvalue типа «указатель на T1» в тип «указатель на T2” [...] и вернуться к исходному типу дает исходное значение указателя. Восходящий указатель никогда не разыменовывается. Все приведения будут выглядеть неправильно: они нужны для обхода синтаксических ограничений языка. Именно для таких вещей и предназначен 5.2p10. - person jthill; 09.07.2012
comment
Интересно, мне нужно будет рассмотреть это подробно и откопать ссылки. Интересно, примете ли вы мое первоначальное прочтение о том, что категория классов, которые было бы законно делать, будет стандартными типами макета, а не только POD? (Также: примечание по терминологии... чем ближе вы подходите к базовому классу, который он преобразует. Кажется, что это немного наоборот, поскольку вы думаете, что термин "база" будет означать самый нижний, но нет... :-/)< /я> - person HostileFork says dont trust SE; 10.07.2012