Почему функция перегрузки не может быть найдена? [dубликат]

Еще одно решение, а не самое быстрое, но кажется довольно хорошим. По крайней мере, у нее нет ветвей. ;)

uint32 x = ...; // 0x00000001 0x0405a0c0 0x00602000 x |= x << 1; // 0x00000003 0x0c0fe1c0 0x00e06000 x |= x << 2; // 0x0000000f 0x3c3fe7c0 0x03e1e000 x |= x << 4; // 0x000000ff 0xffffffc0 0x3fffe000 x |= x << 8; // 0x0000ffff 0xffffffc0 0xffffe000 x |= x << 16; // 0xffffffff 0xffffffc0 0xffffe000 // now x is filled with '1' from the least significant '1' to bit 31 x = ~x; // 0x00000000 0x0000003f 0x00001fff // now we have 1's below the original least significant 1 // let's count them x = x & 0x55555555 + (x >> 1) & 0x55555555; // 0x00000000 0x0000002a 0x00001aaa x = x & 0x33333333 + (x >> 2) & 0x33333333; // 0x00000000 0x00000024 0x00001444 x = x & 0x0f0f0f0f + (x >> 4) & 0x0f0f0f0f; // 0x00000000 0x00000006 0x00000508 x = x & 0x00ff00ff + (x >> 8) & 0x00ff00ff; // 0x00000000 0x00000006 0x0000000d x = x & 0x0000ffff + (x >> 16) & 0x0000ffff; // 0x00000000 0x00000006 0x0000000d // least sign.bit pos. was: 0 6 13
191
задан Rob Kennedy 27 October 2009 в 06:40
поделиться

4 ответа

Судя по формулировке вашего вопроса (вы использовали слово «скрыть»), вы уже знаете, что здесь происходит. Это явление называется «сокрытие имени». По какой-то причине каждый раз, когда кто-то задает вопрос о , почему происходит скрытие имени , люди, которые отвечают, говорят, что это называется «сокрытие имени» и объясняют, как это работает (что вы, вероятно, уже знаете), или объясните как его переопределить (о котором вы никогда не спрашивали), но, похоже, никто не заботится об актуальном вопросе «почему».

Решение, обоснование скрытия имени, т. е. , почему , он фактически был разработан в C ++, заключается в том, чтобы избежать определенного противоречивого, непредвиденного и потенциально опасного поведения, которое может иметь место, если унаследованный набор перегруженных функций был разрешен для смешивания с текущим набором перегрузок в данном классе. Вероятно, вы знаете, что в C + + перегрузка разрешает работу, выбирая наилучшую функцию из набора кандидатов. Это делается путем сопоставления типов аргументов с типами параметров. Правила сопоставления иногда могут быть сложными и часто приводят к результатам, которые могут быть восприняты нелогично неподготовленным пользователем.

Например, базовый класс B имеет функцию-член foo , который принимает параметр типа void * , и все вызовы foo (NULL) разрешены на B :: foo (void *) . Предположим, что скрытие имени отсутствует, и этот B :: foo (void *) отображается во многих разных классах, спускающихся с B . Однако, скажем, у некоторого [непрямого, удаленного] потомка D класса B определена функция foo (int) . Теперь, без скрытия имени D имеет как foo (void *) , так и foo (int) видимые и участвующие в разрешении перегрузки. Какую функцию вызовут вызовы foo (NULL) , если они сделаны через объект типа D ? Они разрешат D :: foo (int) , поскольку int является лучшим совпадением для интегрального нуля (т. Е. [D17] NULL ), чем любой тип указателя , Таким образом, по всей иерархии вызывает foo (NULL) решение одной функции, а в D (и под) они внезапно разрешаются на другую.

Другой пример приведен в . Дизайн и эволюция C ++ , стр. 77:

  class Base {int x;  public: virtual void copy (Base * p) {x = p- & gt;  Икс;  }};  class Derived {int xx;  public: virtual void copy (Derived * p) {xx = p- & gt; xx;  Основание :: копия (р);  }};  void f (База a, Производные b) {a.copy (& amp; b);  // ok: копировать базовую часть b b.copy (& amp; a);  // error: copy (Base *) скрыта копией (Derived *)}  

Без этого правила состояние b будет частично обновлено, что приведет к разрезанию.

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

Как вы правильно заметили в вашем исходном сообщении (я имею в виду замечание «Не полиморфное»), это поведение можно рассматривать как нарушение отношений IS-A между классами. Это правда, но, видимо, тогда было решено, что в конечном итоге сокрытие имен окажется меньшим злом.

359
ответ дан Jason 16 August 2018 в 01:36
поделиться
  • 1
    Да, это реальный ответ на вопрос. Спасибо. Мне тоже было любопытно. – Omnifarious 27 October 2009 в 07:55
  • 2
    Отличный ответ! Также, как практический вопрос, компиляция, вероятно, будет намного медленнее, если поиск по именам должен был пройти весь путь до вершины каждый раз. – Drew Hall 27 October 2009 в 12:24
  • 3
    (Старый ответ, я знаю.) Теперь nullptr Я бы возражал против вашего примера, сказав «если вы хотите вызвать версию void * , вы должны использовать указатель тип & Quot ;. Есть ли другой пример, где это может быть плохо? – GManNickG 17 May 2011 в 20:26
  • 4
    Имя, скрывающееся, на самом деле не злое. & Quot; is-a & quot; отношения все еще существуют и доступны через базовый интерфейс. Таким образом, возможно, d- & gt; foo () не даст вам «Is-a Base », но static_cast & lt; Base * & gt; (d ) - & gt; foo () будет , включая динамическую отправку. – Kerrek SB 9 January 2014 в 15:36
  • 5
    Этот ответ бесполезен, потому что приведенный пример ведет себя так же или не скрывает: D :: foo (int) будет вызван либо потому, что он лучше соответствует, либо потому, что он скрыл B: foo (int). – Richard Wolf 8 July 2014 в 04:06

Правила разрешения имен говорят, что поиск имени останавливается в первой области, в которой найдено совпадающее имя. В этот момент правила разрешения перегрузки срабатывают, чтобы найти наилучшее соответствие доступных функций.

В этом случае gogo (int *) находится (в одиночку) в Derived класс, и поскольку стандартное преобразование из int в int * не выполняется, поиск не выполняется.

Решение состоит в том, чтобы принести объявления Base с помощью объявления using в классе Derived:

  с использованием Base :: gogo;   

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

39
ответ дан Drew Hall 16 August 2018 в 01:36
поделиться
  • 1
    Но почемуyyy ????? – Matt Joiner 8 July 2014 в 04:58
  • 2
    OP: «Почему переопределенная функция в производном классе скрывает другие перегрузки базового класса? & Quot; Этот ответ: «Потому что это так». – Richard Wolf 8 July 2014 в 04:58

Это «По дизайну». В разрешении перегрузки C ++ для этого типа метода работает следующим образом:

  • Начиная с типа ссылки и затем переходя к базовому типу, найдите первый тип, который имеет метод с именем «gogo «
  • Учитывая только методы с именем« gogo »в этом типе, найдите соответствующую перегрузку

. Поскольку Derived не имеет соответствующей функции с именем« gogo », разрешение перегрузки выходит из строя.

13
ответ дан JaredPar 16 August 2018 в 01:36
поделиться

Скрытие имени имеет смысл, потому что оно предотвращает двусмысленности в разрешении имен.

Рассмотрим этот код:

  class Base {public: void func (float x) {..  .}} class Derived: public Base {public: void func (double x) {...}} Derived dobj;   

Если Base :: func (float) не был скрыт Derived :: func (double) в Derived, мы бы назвали функция базового класса при вызове dobj.func (0.f) , хотя float может быть увеличен до double.

Ссылка: http: // bastian .rieck.ru / блог / сообщений / 2016 / name_hiding_cxx /

2
ответ дан Sandeep Singh 16 August 2018 в 01:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: