кастинг шаблонов c ++ - PullRequest
       15

кастинг шаблонов c ++

3 голосов
/ 03 апреля 2009

Я немного растерялся в том, как кастовать шаблоны. У меня есть функция foo, которая принимает параметр типа ParamVector<double>*. Я хотел бы передать ParamVector<float>*, и я не могу понять, как перегрузить оператор приведения для моего ParamVector класса, и Google не сильно мне помогает. У кого-нибудь есть пример как это сделать? Спасибо.

РЕДАКТИРОВАТЬ: Добавление некоторого кода, извините, я идиот и не совсем правильно сформулировал исходный вопрос;

template<class T> class ParamVector 
{
public:
    vector <T> gnome;
    vector <T> data_params;
}

template<class T> class ParamVectorConsumer
{
 public:
 ParamVector<T> test;
}

ParamVector<float> tester;
ParamVectorConsumer<double> cons;
cons.ParamVector = tester

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

РЕДАКТИРОВАТЬ 2: Кастинг был неправильным словом. Я не возражаю против написания дополнительного кода, мне просто нужно знать, как его принять компилятором, чтобы я мог написать какой-то код преобразования.

Ответы [ 8 ]

7 голосов
/ 03 апреля 2009

Я не уверен, но, может быть, вам нужно что-то вроде этого:

template< typename TypeT >
struct ParamVector
{
    template < typename NewTypeT >
    operator ParamVector< NewTypeT >()
    {
        ParamVector< NewTypeT > result;
        // do some converion things
        return result;
    }

    template< typename NewTypeT >
    ParamVector( const ParamVector< NewTypeT > &rhs )
    {
        // convert
    }

    template < typename NewTypeT >
    ParamVector& operator=( const ParamVector< NewTypeT > &rhs )
    {
        // do some conversion thigns
        return *this;
    }


};
ParamVector< double > d1;
ParamVector< float > f1;
f1 = d1;

Вы можете выбрать использовать оператор преобразования или оператор = - я предоставил оба в моем примере.

3 голосов
/ 03 апреля 2009

C ++ FAQ имеет раздел , который очень хорошо объясняет это!

Надеюсь, это поможет!

2 голосов
/ 03 апреля 2009

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

Одним из решений вашей проблемы было бы использование конструктора преобразования:

template<class T> class ParamVector 
{
public:
    vector <T> gnome;
    vector <T> data_params;

    ParamVector()
    {
    }
    template <class T2> ParamVector(const ParamVector<T2> &source)
    {
        gnome.reserve(source.gnome.size());
        copy(source.gnome.begin(), source.gnome.end(), gnome.begin());
        data_params.reserve(source.data_params.size());
        copy(source.data_params.begin(), source.data_params.end(), data_params.begin());
    }
};

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

2 голосов
/ 03 апреля 2009

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

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

Но ни статические, ни динамические типы не имеют никакого отношения.

Позвольте мне вернуться сюда и объяснить.

Когда я объявляю указатель на класс, например

Foo fp*;

fp имеет то, что мы называем статическим типом указателя на Foo. Если класс Bar является подклассом Foo, и я указываю fp на новый Bar:

fp = new Bar1();

тогда мы говорим, что объект, на который указывает fp, имеет динамический тип Bar.

если Bar2 также публично происходит от Foo, я могу сделать это:

fp = new Bar2();

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

Для template< typename T > struct Baz { void doSomething(); };

Baz<int> и Baz<float> - два совершенно разных типов классов , без взаимосвязи.

Единственное «отношение» - это то, что я могу вызывать doSomething () для обоих, но поскольку статические типы не имеют отношения, если у меня есть Baz<int> bi*, я не могу указать его на Baz<float>. Даже с актерами. Компилятор не может «перевести» вызов метода Baz doSotheing в вызов метода Baz :: doSomething (). Это потому, что в нет «метода Baz», нет Baz , есть только Baz<int>s, Baz<float>s и Baz<whatevers>, но нет общего родителя. Baz - это не класс, Baz - это шаблон, набор инструкций о том, как создать класс, если и только если у нас есть параметр T, связанный с фактическим типом (или константой).

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

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

Итак. Это было долго и запутанно. Вне определения шаблона нет никакой связи между ParamVector и aParamVector. Так что ваше задание не может работать.

Хорошо. Почти.

На самом деле, с частичным применением шаблонов, вы можете написать шаблонную функцию , которая дает «рецепт» того, как преобразовать Paramvector<T> в ParamVector<U>. Обратите внимание на T и U. Если вы можете написать код для преобразования любого типа ParamVector, независимо от фактического параметра шаблона, в любой другой тип ParamVector, вы можете упаковать его как частично примененный шаблон, и компилятор добавит эту функцию в Например, ParamVector.

Это, вероятно, включает в себя ParamVector<U> и преобразование каждого T в ParamVector<T> в U для вставки ParamVector<U>. Который все еще не позволит вам присвоить ParamConsumer<T>.

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

1 голос
/ 03 апреля 2009

Вы не можете использовать такие шаблоны, потому что типы не связаны.

Однако вы можете добавить функцию преобразования, например:

(Ваш код не был действительно полным, поэтому я также могу опубликовать полный код. Надеюсь, вы поймете идею.)

template<class T> class ParamVectorConsumer
{
 public:
 ParamVector<T> test;

 template<T2> ParamVectorConsumer<T2> convert()
 {
   ParamVectorConsumer<T2> ret;
   ret = this->...
}
1 голос
/ 03 апреля 2009

Вы упомянули «приведение шаблонов» в своем заголовке, поэтому я предполагаю, что ParamVector является шаблонным типом. Это означает, что foo также может быть шаблонным, и это решит вашу проблему.

template <typename T>
void foo(ParamVector<T> const& data)
{
}
1 голос
/ 03 апреля 2009

Вы не можете сделать это с прямым броском, потому что double и float совершенно разных размеров. Двойные числа будут 64 битами, а плавающие 32. Указатель вынужден приводить из

ParamVector<float>

до

ParamVector<double>

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

Подумайте об этом на секунду, у вас есть один массив, представляющий собой набор из 64-битных значений с полями, расположенными так:

0 => abcdabcd12341234
1 => abcdabcd12341234

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

0 => abcdabcd
1 => 12341234
2 => abcdabcd
3 => abcdabcd

или его можно переключить так, чтобы на первом месте были 12341234, или что-то более странное из-за того, как работает порядок слов.

1 голос
/ 03 апреля 2009

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

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