Функция-член скрывает свободную функцию

void foo(int)
{
}

class X
{
    void foo()
    {
    }

    void bar()
    {
        foo(42);
        // error: no matching function for call to 'X::foo(int)'
        // note: candidate is:
        // note: void X::foo()
        // note:   candidate expects 0 arguments, 1 provided        
    }
};

Почему С++ не может вызвать свободную функцию (которая является единственной с правильной сигнатурой)?

Ответ 1

Логическая причина Консистенция.

  • Предположим, что в соответствии с предложением компилятор разрешает foo(42) на ::foo(int).
  • Теперь, после некоторого времени, если вы измените X::foo() на X::foo(int), тогда foo(42) будет разрешено до X::foo(int). Что несовместимо.

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

Такие случаи можно разрешить двумя способами:

(1) Дайте полное имя (например, ::foo(42))

(2) Используйте утилиту using; например.

void bar()
{
  using ::foo;
  foo(42);
}

Ответ 2

Поскольку два идентификатора определены в разных областях, а разрешение перегрузки касается только функций в той же области. Как только компилятор обнаружит, что класс имеет foo, он перестает подниматься до более широких областей (С++ 11 §3.4.1/1), поэтому свободная функция foo скрыта.

Вам нужно использовать квалифицированное имя для ссылки на глобальный foo:

::foo(42);

Ответ 3

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

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

Ответ 4

Действительно, как ваш вопрос. Также я могу сказать, используйте этот синтаксис:

::foo(42);

Но я могу сказать, что, на мой взгляд, это более элегантное и хорошее программирование, задайте пространства имен, чтобы вы могли написать что-то вроде этого:

namespace MyNameSpace
{
   void foo(int){}

   class X
   {
        void foo(){}

        void bar()
        {
            MyNameSpace::foo(42);
        }
   };
};

Это хорошо, потому что Namespaces позволяет группировать классы, объекты и функции под именем.

PS: Затем это поможет вам понять смысл записи ::foo(42);, если у вас нет пространства имен.

Ответ 5

Я не могу ответить на вопрос о части вашего вопроса - я не знаю, в чем причина этого в спецификации языка.

Чтобы вызвать глобальную функцию в вашем примере, используйте синтаксис:

::foo(42);

Ответ 6

Причиной этого является тот факт, что компилятор сначала ищет подходящее имя функции, игнорируя возвращаемые значения и параметры. Когда внутри класса он попытается найти подходящий элемент там (фактически, он будет просматривать все области, идущие "вверх", локальная область (области), область действия, область класса, область пространства имен, глобальная область действия и т.д.).

X::foo - первое совпадающее имя. THEN (не раньше) попытается выбрать правильную перегрузку (если есть несколько деклараций) на основе параметров (именно по этой причине вы можете перегружать одну и ту же функцию разными параметрами, но не только с разными значениями возврата), а затем она будет проверять возвращаемое значение (если оно есть).