Приватная функция перекрывает публичный интерфейс базового класса

Пример:

Подписчик:

class Subscriber {};

Базовый класс:

class SubscribableChannel
{
public:
    virtual ~SubscribableChannel() = default;

public:
    auto subscribe(Subscriber&) -> void;
    auto unsubscribe(Subscriber&) -> void;

private:
    virtual auto internal_subscribe(Subscriber&) -> void = 0;
    virtual auto internal_unsubscribe(Subscriber&) -> void = 0;
};

auto SubscribableChannel::subscribe(Subscriber& s) -> void
{ internal_subscribe(s); }

auto SubscribableChannel::unsubscribe(Subscriber& s) -> void
{ internal_unsubscribe(s); }

Класс-наследник SubscribableChannel:

class PubSubChannel : public SubscribableChannel
{
public:
    PubSubChannel() = default;

private:
    auto internal_subscribe(Subscriber&) -> void override {};
    auto internal_unsubscribe(Subscriber&) -> void override {};
    // Здесь появляется функция `unsubscribe`
    // имя которой совпадает с именем одной из функций базового класса,
    // но их сигнатуры отличаются: Subscriber& vs (Subscriber&, int)
    auto unsubscribe(Subscriber&, int) -> void {}
};

В результате попытка вызова функции unsubscribe:

PubSubChannel channel;
Subscriber s;
channel.unsubscribe(s);

, приводит к следующей ошибке:

error: no matching function for call to ‘PubSubChannel::unsubscribe(Subscriber&)’

note: candidate: ‘void PubSubChannel::unsubscribe(Subscriber&, int)’

Вопрос: является ли данное поведение допустимым? Если да - дайте, пожалуйста, линк на номер параграфа из стандарта C++.

ENV: linux, clang-10/gcc-10


Ответы (1 шт):

Автор решения: user7860670

Это описывается в

11.8 Member name lookup [class.member.lookup]
2 The following steps define the result of name lookup for a member name f in a class scope C.
3 The lookup set for f in C , called S ( f,C ), consists of two component sets: the declaration set, a set of members named f ; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the set of designated members that are not hidden or overridden by members of the derived class (9.9), and type declarations (including injected-class-names) are replaced by the types they designate. S(f,C) is calculated as follows:
4 If C contains a declaration of the name f , the declaration set contains every declaration of f declared in C that satisfies the requirements of the language construct in which the lookup occurs.
[Note: Looking up a name in an elaborated-type-specifier (6.5.4) or base-specifier (11.7), for instance, ignores all non-type declarations, while looking up a name in a nested-name-specifier (6.5.3) ignores function, variable, and enumerator declarations. As another example, looking up a name in a using-declaration (9.9) includes the declaration of a class or enumeration that would ordinarily be hidden by another declaration of that name in the same scope. —end note]
If the resulting declaration set is not empty, the subobject set contains C itself, and calculation is complete.
5 Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S ( f,C ) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject B i , and merge each such lookup set S(f,B i ) in turn into S(f,C).

Так как в PubSubChannel есть объявление unsubscribe, то поиск в базовых классах не производится.

→ Ссылка