Почему поиск имени функции-члена остается в родительском классе? - PullRequest
2 голосов
/ 13 марта 2019

Мотивация, если она помогает: У меня есть функции-члены struct, которые являются ядрами радиальных базисных функций.Они называются 1e06 x 15 x 1e05 раз в численном моделировании.Я рассчитываю на девиртуализацию встроенных виртуальных функций - это не то, что я хочу сделать для такого количества вызовов функций.Кроме того, структуры (ядра RBF) уже используются в качестве параметров шаблона большего класса интерполяции.

Минимальный рабочий пример

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

Функция g() вызывает функцию f(), которая отличается в производных классах.

Я не хочу использовать функции virtual для разрешения имен функций во время выполнения, потому что это сопряжено с дополнительными затратами (я измерял это в своем коде, это дает эффект).

Вот пример:

#include <iostream>

struct A 
{
    double f() const { return 0; };

    void g() const
    {
        std::cout << f() << std::endl; 
    }
};

struct B : private A
{
    using A::g; 
    double f() const { return 1; };
};

struct C : private A
{
    using A::g;
    double f() const { return 2; };
};

int main()
{
    B b; 
    C c; 

    b.g(); // Outputs 0 instead of 1 
    c.g(); // Outputs 0 instead of 2
}

Я ожидал, что механизм разрешения имен выяснит, что я хочу использовать «A :: g ()», но затем вернуться к «B»или «C» для разрешения функции «f ()».Что-то наподобие: «когда я знаю тип во время компиляции, я попытаюсь сначала разрешить все имена в этом типе и выполнить поиск имени по объектам / родителям, чего-то не хватает, а затем вернуться к типу, из которого меня вызывали».Однако, похоже, выясняется, что используется «A :: g ()», затем он сидит в «A» и просто выбирает «A :: f ()», хотя фактический вызов «g ()» поступил из«Б» и «С».

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

Как заставить это работать без виртуальных функций?

1 Ответ

5 голосов
/ 13 марта 2019

Это стандартная задача для CRTP .Базовый класс должен знать, что представляет собой статический тип объекта, и затем он просто приводит себя к этому.

template<typename Derived>
struct A
{
    void g() const
    {
        cout << static_cast<Derived const*>(this)->f() << endl;
    }
};

struct B : A<B>
{
    using A::g; 
    double f() const { return 1; };
};

Кроме того, отвечая на комментарий, который вы написали (возможно, это ваш реальный вопрос?),

Можете ли вы сказать мне, в чем причина того, что поиск имени придерживается базового класса, вместо того, чтобы возвращаться к производному классу после того, как он посмотрел g ()?

Поскольку классы предназначены для использования в объектно-ориентированном программировании, а не для повторного использования кода.Программист A должен уметь понимать, что делает их код, а это означает, что подклассы не должны иметь возможность произвольно переопределять функциональность базового класса.Вот что действительно означает virtual: A дает своим подклассам разрешение переопределять этого конкретного члена .Все, к чему A не присоединился, они должны иметь возможность полагаться.

Рассмотрим в вашем примере: что, если автор B позже добавит целочисленный член, который случился сназываться endl?Должен ли это сломаться A?Должен ли B заботиться обо всех личных именах членов A?И если автор A хочет добавить переменную-член, смогут ли они сделать это таким образом, чтобы не нарушить некоторый подкласс?(Ответы «нет», «нет» и «да».)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...