2012-01-28 15 views
10
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   
    } 
}; 

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

+2

В этом случае вы можете использовать ':: foo (42)' для доступа к внешнему foo. [Идеальная демонстрация] (http://ideone.com/6HljO). Но я мало знаю об пространствах имен. –

+0

Я считаю, что это одна ахиллесова пята C++. Он делает изящное использование общих перегруженных свободных имен функций невозможным, например, isempty (вещь), где есть много перегрузок для упущенных данных типов вещей, при этом допускается существование вещи. Isempty(). Глупый, несчастный, неуклюжий. – Mordachai

ответ

5

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

  • Пусть согласно предложению, компилятор разрешает 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); 
} 
+1

Если кто-то позже добавляет член foo (int), то они явно намереваются это сделать. Плохой дизайн языка, ИМО. – Mordachai

12

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

Вы должны использовать полное имя для обозначения глобального foo:

::foo(42); 
+1

Примечание: это конкретный случай из-за 'int', в большинстве случаев он все еще работает, потому что ADL хорош. –

0

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

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

::foo(42); 
0

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

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

1

Действительно как и ваш вопрос.Кроме того, я мог бы сказать, используйте следующий синтаксис:

::foo(42); 

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

namespace MyNameSpace 
{ 
    void foo(int){} 

    class X 
    { 
     void foo(){} 

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

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

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

2

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

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

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