Рассмотрим этот код:
struct A
{
void foo() const
{
std::cout << "const" << std::endl;
}
private:
void foo()
{
std::cout << "non - const" << std::endl;
}
};
int main()
{
A a;
a.foo();
}
Ошибка компилятора:
ошибка: 'void A :: foo ()' является закрытым '.
Но когда я удаляю частный, он просто работает. Почему общедоступный метод const не вызывается, если неконстантный метод является частным?
Другими словами, почему разрешение перегрузки предшествует контролю доступа? Это странно. Как вы думаете, это последовательно? Мой код работает, а затем я добавляю метод, и мой рабочий код вообще не компилируется.
Ответы:
Когда вы вызываете
a.foo();
, компилятор выполняет разрешение перегрузки, чтобы найти лучшую функцию для использования. Когда он создает набор перегрузки, он находити
Теперь, поскольку
a
это не такconst
, лучше всего подходит неконстантная версия, поэтому выбирает компиляторvoid foo()
. Затем устанавливаются ограничения доступа, и вы получаете ошибку компилятора, так какvoid foo()
это частный.Помните, что при разрешении перегрузки это не «поиск наиболее подходящей функции». Это «найди лучшую функцию и попробуй ее использовать». Если это невозможно из-за ограничений доступа или удаления, вы получите ошибку компилятора.
Что ж, посмотрим:
Теперь предположим, что я на самом деле не имел в виду
void foo(Derived * d)
приватность. Если бы сначала был контроль доступа, эта программа компилировалась бы, запускалась иBase
печаталась. Это может быть очень сложно отследить в большой кодовой базе. Поскольку контроль доступа происходит после разрешения перегрузки, я получаю красивую ошибку компилятора, сообщающую мне, что функция, которую я хочу, чтобы она вызывала, не может быть вызвана, и я могу найти ошибку намного проще.источник
В конечном итоге это сводится к утверждению в стандарте, что доступность не должна приниматься во внимание при выполнении разрешения перегрузки . Это утверждение можно найти в пункте 3 [over.match] :
а также примечание в пункте 1 того же раздела:
Что касается причины, я могу придумать несколько возможных мотивов:
источник
Предположим, что управление доступом предшествовало разрешению перегрузки. Фактически это будет означать, что
public/protected/private
видимость будет контролироваться, а не доступность.В разделе 2.10 « Проектирования и эволюции C ++» Страуструпа есть отрывок по этому поводу, в котором он обсуждает следующий пример.
Страуструп упоминает о том , что польза от нынешних правил (видимость до того доступности) является то , что (временно) чейнинга на
private
внутреннююclass X
вpublic
(например , для целей отладки) является то , что нет никаких изменений тихо в значении выше программы (т.е.X::a
предпринята попытка быть доступным в обоих случаях, что дает ошибку доступа в приведенном выше примере). Еслиpublic/protected/private
бы контролировать видимость, значение программы изменилось бы (globala
будет вызываться с помощьюprivate
, иначеX::a
).Затем он заявляет, что не помнит, было ли это явным дизайном или побочным эффектом технологии препроцессора, использованной для реализации C с предшественником Classess для Standard C ++.
Как это связано с вашим примером? В основном потому, что стандартное разрешение перегрузки соответствует общему правилу, согласно которому поиск имени выполняется перед контролем доступа.
источник
Поскольку неявный
this
указатель не являетсяconst
, компилятор сначала проверяет наличие не-const
версии функции передconst
версией.Если вы явно отметите не
const
один,private
разрешение не будет выполнено, и компилятор не продолжит поиск.источник
Важно помнить о порядке происходящего, а именно:
delete
d), завершиться ошибкой.(3) происходит после (2). Что действительно важно, потому что в противном случае создание функций
delete
d илиprivate
стало бы бессмысленным, и о котором было бы намного труднее думать.В таком случае:
A::foo()
иA::foo() const
.A::foo()
что последняя включает в себя квалификационное преобразование неявногоthis
аргумента.A::foo()
есть,private
и у вас нет к нему доступа, следовательно, код плохо сформирован.источник
Это сводится к довольно простому дизайнерскому решению на C ++.
При поиске функции для удовлетворения вызова компилятор выполняет поиск следующим образом:
Он ищет первую 1 область, в которой есть что- то с таким именем.
Компилятор находит в этой области все функции (или функторы и т. Д.) С этим именем.
Затем компилятор выполняет разрешение перегрузки, чтобы найти лучшего кандидата среди найденных (независимо от того, доступны они или нет).
Наконец, компилятор проверяет, доступна ли выбранная функция.
Да, из-за такого порядка компилятор может выбрать недоступную перегрузку, даже если есть другая доступная перегрузка (но не выбранная во время разрешения перегрузки).
Что касается того, можно ли поступить иначе: да, несомненно. Однако это определенно привело бы к совершенно другому языку, чем C ++. Оказывается, многие, казалось бы, довольно второстепенные решения могут иметь разветвления, влияющие на гораздо большее, чем может быть изначально очевидно.
источник
Контроль доступа (
public
,protected
,private
) не влияет на перегрузки разрешения. Компилятор выбирает,void foo()
потому что это лучшее соответствие. Тот факт, что он недоступен, не меняет этого. При его удалении остается толькоvoid foo() const
лучшее (то есть единственное) совпадение.источник
В этом звонке:
В
this
каждой функции-члене всегда есть неявный указатель. Иconst
квалификацияthis
берется из вызывающей ссылки / объекта. Вышеупомянутый вызов обрабатывается компилятором как:Но у вас есть два объявления,
A::foo
которые рассматриваются как :По разрешению перегрузки первый будет выбран для неконстантного
this
, второй будет выбран дляconst this
. Если вы удалите первый, второй будет привязан к обоимconst
иnon-const
this
.После разрешения перегрузки для выбора наиболее жизнеспособной функции наступает контроль доступа. Поскольку вы указали доступ к выбранной перегрузке как
private
, компилятор пожалуется.В стандарте сказано так:
Но если вы сделаете это:
Тогда
const
подойдет только перегрузка.источник
На техническую причину ответили и другие ответы. Я сосредоточусь только на этом вопросе:
Так был разработан язык. Намерение пытается вызвать наилучшую жизнеспособную перегрузку, насколько это возможно. Если это не удастся, появится сообщение об ошибке, чтобы напомнить вам о необходимости еще раз рассмотреть дизайн.
С другой стороны, предположим, что ваш код скомпилирован и хорошо работает с
const
вызываемой функцией-членом. Когда-нибудь кто-то (может быть, вы сами) решит изменить доступность функции, не являющейсяconst
членом, сprivate
наpublic
. Тогда поведение изменится без ошибок компиляции! Это было бы сюрпризом .источник
Поскольку переменная
a
вmain
функции не объявлена какconst
.Постоянные функции-члены вызываются для постоянных объектов.
источник
Спецификаторы доступа никогда не влияют на поиск имени и разрешение вызова функций. Функция выбирается до того, как компилятор проверит, должен ли вызов вызвать нарушение прав доступа.
Таким образом, если вы измените спецификатор доступа, вы будете предупреждены во время компиляции, если в существующем коде есть нарушение; если бы конфиденциальность принималась во внимание при разрешении вызовов функций, поведение вашей программы могло бы незаметно измениться.
источник