Шаблоны функций-членов не могут быть объявлены виртуальными - из Addison Wesley: C ++ Templates - PullRequest
16 голосов
/ 21 апреля 2011

Из Addison Wesley: Шаблоны C ++

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

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

Ответы [ 6 ]

24 голосов
/ 21 апреля 2011

Да, и нет.

Самый популярный метод для разрешения вызовов виртуальных функций - это использование таблицы ("vtable"), где каждая виртуальная функция отображается на индекс в таблице.Это более или менее требует, чтобы вы знали размер таблицы.

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

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

Итак, в конце концов, было решено, что вводить виртуальные шаблоны в язык просто не стоит.

7 голосов
/ 21 апреля 2011

Рассмотрим:

struct X
{
    template <typename T>
    T incr(const T& t)
    {
        return t + 1;
    }
};

Поскольку incr() применяется к различным типам T, генерируются новые функции.Скажем, внутри app.c++ у вас есть:

X x;
x.incr(7);        // incr<int>()
x.incr(7.0);      // incr<double>()
x.incr("hello");  // incr<const char*>()

Затем, когда он компилирует app.c++, он видит 3 функции, которые - если бы incr было разрешено быть virtual - могли бы освободить место для трехприведенные выше экземпляры в таблице виртуальной диспетчеризации для X. Затем скажите, что она загружает общую библиотеку во время выполнения, и код для этой библиотеки имеет 2 экземпляра X::incr для uint32_t и std::string::const_iterator.dlopen() потребуется увеличить существующую таблицу виртуальной отправки для уже созданных объектов, чтобы освободить место для двух новых функций.Звучит не слишком ужасно, но учтите:

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

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

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

4 голосов
/ 21 апреля 2011

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

В основном да. Более конкретно, статическая привязка вызывает проблему, когда код генерируется для поддержки динамической привязки.

Когда компилятор компилирует базовый класс, он находит виртуальную функцию и решает создать таблицу виртуальных функций - это будет использоваться для реализации динамического связывания: когда виртуальная функция вызывается для производного экземпляра, скомпилированный код следует за указатель в экземпляре на таблицу виртуальных функций для производного класса, затем указатель в этой таблице на реализацию виртуальной функции. Эта таблица должна включать все возможные виртуальные функции, которые могут быть вызваны. Теперь предположим, что мы создали шаблонную виртуальную функцию. Таблица функций будет нуждаться в записи для каждого экземпляра шаблона, потому что любая из этих функций может быть вызвана во время выполнения. Но информация о том, с какими типами создается шаблон, не может (вообще) быть собрана во время создания таблицы виртуальных функций. (По крайней мере, не без игры с моделью компиляции C ++.)

1 голос
/ 21 апреля 2011

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

template<class T>
class A { virtual void f()=0; }; // works fine

class A { template<class T> virtual void f(T t)=0; }; // does not work
0 голосов
/ 21 апреля 2011

Сорта.

Вы не можете "переопределить" необоснованный template, потому что его даже нет в скомпилированном приложении.Если вы создаете его экземпляр, значит, вы не переопределяете шаблон, а просто еще одну обычную функцию.: -)

0 голосов
/ 21 апреля 2011

Зависит от того, что вы подразумеваете под связыванием.

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

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