указание типа члена класса в функции шаблона

#c #templates #traits

#c #шаблоны #Трейты

Вопрос:

У меня есть эта функция шаблона:

 template <class P>
double Determinant(const P amp; a, const P amp; b, const P amp; c) {
    return  (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
  

но я не хочу постоянно указывать возвращаемый тип double — P:: x и P:: y также могут быть целыми числами, и мне нужна эта функция в обеих ситуациях. Есть ли способ указать тип x и y, что-то вроде этого?

 //doesn't compile; can't deduce template argument for T
template <typename T, class P>
T Determinant(const P amp; a, const P amp; b, const P amp; c) {
    return  (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
  

редактировать: Мой компилятор — VC2005

правка 2: извините, что забыл упомянуть: К сожалению, я не могу изменить реализацию структур для P; одним из типов точек, с которыми я имею дело, является CPoint MFC / ATL, которые жестко запрограммированы как { long x; long y; } .

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

1. Какой компилятор (ы) вы используете?

2. vc2005 — теперь отредактировано в вопросе.

Ответ №1:

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

В C 03 вы можете определить typedef в своем классе как:

 struct A //suppose A is going to be type argument to your function template
{
   int x, y;     //I'm assuming type of x and y is same!
   typedef int value_type; //type of x and y!
};
  

И затем вам придется переписать свою функцию как:

 template <class P>
typename P::value_type Determinant(const P amp; a, const P amp; b, const P amp; c) {
    return  (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
  

Теперь обратите внимание на возвращаемый тип, это зависимый тип. Его :

 typename P::value_type
  

Здесь требуется ключевое слово typename .


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

 template<typename T> struct PTraits;

//Suppose this is your type which you can't modify
struct A //A is going to be type argument to your function template
{
   long x, y; 
};

//specialization: defining traits for struct A
template<>
struct PTraits<A>
{
    typedef long value_type; //since type of A::x and A::y is long!
};
  

И ваш шаблон функции будет выглядеть следующим образом:

 template <class P>
typename PTraits<P>::value_type Determinant(const P amp; a, const P amp; b, const P amp; c) {
    return  (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
  

Обратите внимание на возвращаемый тип; теперь он немного отличается:

 typename PTraits<P>::value_type
  

Опять же, value_type это зависимое имя, поэтому ключевое слово typename является обязательным.

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

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

1. отличный трюк, Наваз; не думал об этом раньше. К сожалению, я не могу изменить реализацию структуры; одним из типов точек, с которыми я имею дело, является CPoint MFC / ATL, которые жестко запрограммированы как { long x; long y; } , без полезных определений типов такого рода.

2. @martin_ljchan: Смотрите второе решение!

3.@martin_ljchan: Вы можете начать отсюда: Особенности: новая и полезная шаблонная техника Натана К. Майерса. Я думаю, что именно он первым описал эту технику. 🙂

Ответ №2:

Мне нравится использовать подход в стиле черт для этого:

 template<typename T> struct DeterminantReturnInfo {};  
template<> struct DeterminantReturnInfo<MyType> { typedef MyOtherType ReturnType; }

template< typename T >
typename DeterminantReturnInfo<T>::ReturnType Determinant( const P amp; a, const P amp; B, const P amp; c)
{
  return  (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}
  

Если вы хотите, чтобы по умолчанию он был равен double, тогда вы просто добавляете typedef double ReturnType; к исходному шаблону.

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

1. typedef ReturnType double; ? Неправильный синтаксис

2. Действительно. Исправлено. (Хотя это очевидная опечатка, которую вы могли бы исправить с помощью кнопки редактирования)

Ответ №3:

Если вы используете Visual Studio 2010 или GCC 4.5 , вы можете использовать форму завершающего типа возвращаемого значения:

 template<class P>
auto fun(const Pamp; a) -> decltype(a.x   a.y){
  return a.x   a.y;
}
  

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

Ответ №4:

Проверьте возвращаемый тип его переменной-члена типа x / y. Вы можете не возвращать so тип T.