Как вывести функцию шаблона? ИЛИ Каков предпочтительный подход для этого сценария? - PullRequest
2 голосов
/ 27 января 2012

У меня есть две функции

MultiplyVerison1(T x, T y); // in class A
MultiplyVersion1(T x, T y); // in class B

Выше функции находятся в отдельных не шаблонных классах.

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

Итак, как мы можем добиться того же с помощью шаблонных функций?

1 Ответ

3 голосов
/ 27 января 2012

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

Вы можете думать об этом как о том, что вызов вызываетсясоздание экземпляра шаблона функции с конкретным типом T - если вы вызываете его с помощью int, но динамический тип объекта, к которому вы его вызываете, неизвестен до времени выполнения (будь то A, B иличто-то еще), тогда компилятор не сможет узнать, что ему нужно создать экземпляр A::MultiplyVersion1<int> или B::MultiplyVersion1<int> или что-то еще.На самом деле есть нечто большее, чем это, но я думаю, что этого достаточно.

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

struct Base {
    template <typename T>
    void MultiplyVersion1(const T &x, const T &y) {
        A *athis = dynamic_cast<A*>(this);
        if (athis) {
            athis->MultiplyVersion1(x,y);
        } else {
            B *bthis = dynamic_cast<B*>(this);
            if (bthis) {
                bthis->MultiplyVersion1(x,y);
            } else {
                throw std::logic_error();
            }
        }
    }
    virtual ~Base() {}
};

Теперь, когда вы вызываете MultiplyVersion1<int> через указатель на базу, создаются экземпляры A::MultiplyVersion1<int> и B::MutiplyVersion1<int>.Но, конечно, вы не можете легко добавить новые производные классы, что является серьезным ограничением.

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

Если все, что вам нужно от базового класса, это повторное использование кода для некоторых других функций, то вам не нужен динамический полиморфизм.Полностью исключите MultiplyVersion1 из базового класса (и, возможно, не наследуйте публично от Base, вместо этого унаследуйте конфиденциально и добавьте функции, которые вы хотите повторно использовать с операторами using).Если функции, которые вы хотите определить для повторного использования, вызывают MultiplyVersion1, рассмотрите моделируемое динамическое связывание через CRTP:

#include <iostream>

template <typename Derived>
struct Base {
    template <typename T>
    void MultiplyVersion2(const T &x, const T &y) {
        static_cast<Derived&>(*this).MultiplyVersion1(x + 1, y + 1);
    }
};

struct A : private Base<A> {
    friend class Base;
    template <typename T> void MultiplyVersion1(T x, T y) {
        std::cout << x*y << "\n"; 
    }
    using Base::MultiplyVersion2;
};

struct B : private Base<B> {
    friend class Base;
    template <typename T> void MultiplyVersion1(T x, T y) {
        std::cout << x << " * " << y << " = " << x*y << "\n"; 
    }
    using Base::MultiplyVersion2;
};

int main() {
    A a;
    a.MultiplyVersion2(1,2);
    B b;
    b.MultiplyVersion2(1,2);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...