2011-08-26 2 views
13

Что такое стандарты C++ 98/C++ 03 и точные правила будущего стандарта C++ 0x для dominance in virtual inheritance?Доминирование в виртуальном наследовании

Я не прошу только о конкретных пунктах, хотя я также прошу об этом (где-то в разделе 10, я бы предположил).

Я спрашиваю также о последствиях стандартного стандарта, пояснил стандарт.

+0

Что вы подразумеваете под «доминированием» в отношении виртуального наследования? –

+1

@Nicol: индекс стандарта C++ 98 относится к виртуальному наследованию и на странице 167. это все стандарт говорит напрямую (и, насколько я знаю, индекс ненормативный). объяснение доминирования займет больше, чем этот комментарий, так что просто google it или [wikipedia] (http://en.wikipedia.org/wiki/Dominance_%28C%2B%2B%29) - если вы не знаете Об этом, вероятно, вы никак не можете ответить на этот вопрос. cheers, –

+0

@Nicol: спасибо за вопрос, я добавил ссылку на вопрос. –

ответ

25

Я думаю, что это тот язык, который вы ищете. В C++ 03 спецификации ISO, в § 10,2/2, мы имеем следующее:

Следующие шаги определяют результат поиска имен в области видимости класса, С.-первых, каждая декларация по имени в классе и в каждом из его под-объектов базового класса. Имя члена f в одном под-объекте B скрывает имя члена f в под-объекте A, если A является под-объектом B класса. Любые объявления, которые настолько скрыты, исключаются из рассмотрения. Каждое из этих объявлений, которое было введено с использованием объявления , считается из каждого под-объекта C, который относится к типу, содержащему декларацию, обозначенную декларацией use. Если результирующий набор объявлений не все из под-объектов того же типа, либо набор имеет нестатический член и включает в себя элементы из отдельных под-объектов, существует неопределенность и неформальная программа . В противном случае этот набор является результатом поиска.

На высоком уровне это означает, что при попытке поиска имени оно выглядит во всех базовых классах и самом классе, чтобы найти объявления для этого имени. Затем вы проходите класс за классом, и если один из этих базовых объектов имеет что-то с этим именем, он скрывает все имена, введенные в любой из базовых классов этого объекта.

Важная деталь здесь эта линия:

Любые заявления , которые так скрыты исключаются из рассмотрения.

Важно отметить, что это говорит о том, что если что-то скрыто ничем, это считается скрытым и удалены. Так, например, если я это сделать:

      class D { 
          public: 
           void f(); 
          } 

    class B: virtual public D {  class C: virtual public D { 
    public:       public: 
     void f();       /* empty */ 
    };         }; 

         class A: public B, public C { 
         public: 
          void doSomething() { 
           f(); // <--- This line 
          } 
         }; 

На указанной линии, вызов f() решается следующим образом. Сначала добавим B::f и D::f к набору имен, которые можно было бы рассмотреть. D::f ничего не скрывает, потому что D не имеет базовых классов. Однако B::f скрыть D::f, так что даже если D::f может быть достигнут от A, не видя B::f, он считается скрытым и удаленным из множества объектов, которые можно назвать f. Поскольку осталось только B::f, это тот, который называется. Спецификации ИСО упоминает (§ 10.2/7), что

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

Я думаю, что это из-за вышеуказанного правила.

В C++ 11 (в соответствии с проектом спецификации N3242) правила изложены гораздо более явно, чем раньше, и предоставляется фактический алгоритм для вычисления того, что означает название. Вот язык, шаг за шагом.

Начнет с § 10.2/3:

Множество поиска для F в C, называется S (F, С), состоит из двух компонентов наборов: декларации установить, набор членов с именем f; и подобъект , установленный, набор подобъектов, в которых были найдены объявления этих членов (возможно, включая использование-деклараций). В наборе объявлений использование-объявления заменяются членами, которые они обозначают , а объявления типа (включая имена вложенных классов) заменяются типами, которые они обозначают. S (F, С) рассчитывается следующим образом:

В этом контексте C относится к области, в которой происходит поиск. В других словах набор S(f, C) означает «какие объявления видны, когда я пытаюсь найти f в классе scope C?» Чтобы ответить на это, спецификация определяет алгоритм для определения этого. Первый шаг заключается в следующем: (§ 10,2/4)

Если С содержит объявление имени F, множество декларации содержит все декларации е объявленные в C, которая удовлетворяет требования языка построить в который происходит при поиске. [...] Если результирующий набор объявлений не пуст, набор подобъектов содержит сам C , и вычисление завершено.

Другими словами, если сам класс имеет то, что называется f заявленную в нем, то множество декларации только множество вещей, названных f определяется в этом классе (или импортирована с using декларации). Но если мы не сможем найти что-либо с именем f или если все имя с именем f имеет неправильный вид (например, объявление функции, когда мы хотим получить тип), переходим к следующему шагу: (§ 10.2/5)

В противном случае (то есть, C не содержит декларации f или результирующий набор объявлений пуст), S (f, C) изначально пуст. Если C имеет базовые классы, вычислите набор поиска для f в каждом подобъекте прямого прямого базового класса B i и объедините каждый такой набор поиска S (f, B i), в свою очередь, в S (f, C).

Другими словами, мы рассмотрим базовые классы, вычислим, на что имя может ссылаться в этих базовых классах, а затем объединить все вместе. Фактический способ слияния указан на следующем шаге. Это очень сложно (у него три части), так что вот удар по дуну. Вот оригинальная формулировка: (§ 10.2/6)

Следующие шаги определяют результат поиска слияния множество S (F, В я) в промежуточные S (F, С):

  • Если каждый из субобъекты S (f, B i) является подобъектом базового класса, по меньшей мере, одного из подобъектов членов S (f, C), или если S (f, B i) пуст, S (f, C) не изменяется и слияние завершено. И наоборот, если каждый из подобъектных элементов S (f, C) является подобъектом базового класса, по меньшей мере, одного из подобъектов S (f, B i) или если S (f, C) пуст , новый S (f, C) является копией S (f, Bi).

  • В противном случае, если объявление множества S (F, B я) и S (F, С) различаются, слияние неоднозначно: новый S (F, С) представляет собой поиск установить с недействительный набор объявлений и объединение наборов подобъектов. В последующих сливается, недопустимый набор объявлений считается отличным от любого другого.

  • В противном случае новый S (f, C) представляет собой набор поиска с общим набором объявлений и объединением наборов подобъектов .

Хорошо, давайте кусочкам это, кроме одного в то время. Первое правило здесь состоит из двух частей. В первой части говорится, что если вы пытаетесь объединить пустой набор объявлений в общий набор, вы ничего не сделаете. В этом есть смысл. В нем также говорится, что если вы пытаетесь объединить что-то в этом базовом классе всего, что уже слито до сих пор, то вы ничего не делаете. Это важно, потому что это означает, что если вы что-то спрятали, вы не хотите случайно повторно вводить его, объединив его обратно.

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

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

Последнее правило гласит, что если мы не находимся ни в одном из этих особых случаев, ничего не получается, и вы должны просто объединить их.

Фу ... это было сложно! Давайте посмотрим, что произойдет, когда мы проследим это за наследование алмазов выше. Мы хотим найти имя f, начиная с A. Поскольку A не определяет f, мы вычисляем значения поиска f, начиная с B и f, начиная с C. Давай посмотрим что происходит. При вычислении значения f означает в B, мы видим, что определено B::f, и поэтому мы перестаем искать.Значение отрываясь f в B есть множество (B::f, B}. Для того, чтобы посмотреть, что f означает C, мы смотрим в C и видим, что он не определяет f, поэтому мы снова рекурсивно искать значение от D. Выполнение поиска в D приводит к {D::f, D}, и когда мы объединим все вместе, мы обнаруживаем, что применяется вторая половина правила 1 (поскольку это пустое значение, поскольку каждый объект в подобъекте является базой D), поэтому окончательный значение для C дано по {D::f, D}.

F мы должны объединить значения для B и C. Это пытается слить {, D} и {B::f, B}. Это - то, где это получает удовольствие. Предположим, мы слились в этом порядке. Слияние {D::f, D} и пустой набор производит {D::f, D}. Когда мы сливаемся в {B::f, B}, то из-за того, что D является базой B, ко второй половине первого правила мы переопределяем наш старый набор и заканчиваем {B::f, B}. Следовательно, поиск f - это версия f в B.

Если, с другой стороны, мы сливаемся в обратном порядке, мы начинаем с {B::f, B} и попробуйте сливаясь {D::f, D}. Но так как D является базой B, мы просто игнорируем его, оставляя {B::f, B}. Мы пришли к такому же результату. Довольно круто, да? Я поражен, что так хорошо получается!

Итак, у вас есть это - старые правила действительно (иш) прямолинейны, и новые правила невероятно сложны, но каким-то образом удается все же выработать.

Надеюсь, это поможет!

+0

@Alf P. Steinbach - я только что обновил это с правилами для C++ 11, а человек - это doozy. Надеюсь, это поможет объяснить вещи (?)! – templatetypedef

+0

+1 для болота в этом. Думаю, теперь я понял часть C++ 98. ОК. Удивительно просто. Тем не менее, мои глаза застекляли, когда я начал читать часть C++ 11 ... Cheers, –

+2

+1 просто для усилий, которые, должно быть, вошли в этот ответ o_O – ildjarn

Смежные вопросы