Обрабатывает ли C созданный шаблонный класс иначе, чем не шаблонный тип?

#c #templates #types #virtual

#c #шаблоны #типы #виртуальный

Вопрос:

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

Вот пример некоторых классов, созданных с использованием шаблонов:

 
template <typename T=void> struct Adder;

template <> struct Adder <void>
{
    virtual double add(double a, double b)
    {
        return (a b);
    }
};

template <typename T> struct Adder : Adder <void>
{
    virtual double add(double a, double b)
    {
        return ((T)a (T)b);
    }
};

    
using AdderBase   = Adder <void>;
using AdderInt    = Adder <int>;
using AdderFloat  = Adder <float>;
using AdderDouble = Adder <double>;

int main(int argc, char* argv[])
{
    AdderBase* addI = new AdderInt;
    AdderBase* addF = new AdderFloat;
    AdderBase* addD = new AdderDouble;

    printf("addI = .1fn", addI->add(11111111111111111.0, 1.0));
    printf("addF = .1fn", addF->add(11111111111111111.0, 1.0));
    printf("addD = .1fn", addD->add(11111111111111111.0, 1.0));
}
 

И вот пример классов с той же функциональностью, созданных без шаблонов:

 
struct AdderBase
{
    virtual double add(double a, double b)
    {
        return (a b);
    }
};

struct AdderInt : AdderBase
{
    virtual double add(double a, double b)
    {
        return ((int)a (int)b);
    }
};

struct AdderFloat : AdderBase
{
    virtual double add(double a, double b)
    {
        return ((float)a (float)b);
    }
};

struct AdderDouble : AdderBase
{
    virtual double add(double a, double b)
    {
        return ((double)a (double)b);
    }
};
 

Обрабатывает ли компилятор классы AdderBase, AdderInt, AdderFloat или AdderDouble по-разному, когда они определены этими двумя разными способами?

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

Комментарии:

1. «классы, определенные таким образом, обрабатываются по-другому» , вероятно, нет. Но почему вам нужно, чтобы базовый класс был специализацией шаблона? Не было бы более удобным для чтения иметь для него отдельный класс?

2. » При использовании виртуальных функций часто удобно использовать один шаблон класса как для базового класса, так и для всех производных классов » Почему? Что в этом «удобного»?

3. «используйте один шаблон класса как для базового класса, так и для всех производных классов» — хотя вы используете одно и то же имя шаблона как для базового класса, так и для производных классов, я бы не описал ваш код как использующий один шаблон класса. Технически это так, но все еще необходимо поддерживать два определения — одно для Adder <void> специализации, а другое для общего случая. Это не «ощущается» иначе, чем использование не-шаблона для базового класса.

4. Не могли бы вы привести пример того, что может повлечь за собой различная обработка классов? О чем вы беспокоитесь? (Я мог бы ожидать какого-то сбоя синтаксиса шаблона, но using псевдонимы должны предотвращать это.)

Ответ №1:

Короткий ответ — нет.

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

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