C ++ возвращает более точные два аргумента шаблона из функции? - PullRequest
6 голосов
/ 01 февраля 2011

Мне любопытно, есть ли способ сделать это в C ++.Допустим, у меня есть шаблонный векторный класс:

template <typename T>
class vector {          
public:
      vector(T a, T b, T c) : x(a), y(b), z(c) {}

      T x,y,z;
};

А затем у меня есть оператор сложения с шаблонами:

template <typename A, typename B> 
vector<A> operator +(const vector<A> &a, const vector<B> &b) { 
   return vector<A>(a.x+b.x, a.y+b.y, a.z+b.z); 
}

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

Например:

vector<float>       + vector<double> would produce a vector<double>, 
vector<long double> + vector<float>  would produce a vector<long double>

Я полагаю, что в C ++ нет автоматической поддержки для этого, но я решил спросить.

Ответы [ 9 ]

9 голосов
/ 01 февраля 2011

Нет встроенной поддержки в виде библиотеки, но вы можете сделать это, используя условный оператор (?:).

В ответ на другой ответ Йоханнес Шауб опубликовал шаблон promote<T, U> , который довольно неплохо оборачивает логику. С помощью шаблона вы должны написать:

template <typename A, typename B>  
vector< typename promote<A, B>::type >
operator+(const vector<A> &a, const vector<B> &b) 
{     
    return vector< typename promote<A, B>::type >(a.x+b.x, a.y+b.y, a.z+b.z);  
} 
7 голосов
/ 01 февраля 2011

в C ++ 0x, вы можете сказать:

template <typename A, typename B> 
auto operator +(const vector<A> &a, const vector<B> &b) -> vector<decltype(a.x + b.x)>
{
    //...
}

В C ++ 03 вам нужно определить все комбинации самостоятельно, хотя вы можете сделать это в схеме многократного использования op_traits, которая может применяться к различным операторам. Джеймс МакНеллис приводит некоторые подробности об этом в своем ответе

3 голосов
/ 01 февраля 2011

Андрей Александреску обсуждал это в своей статье DDJ от 1 апреля 2001 года Общее: Мин и Макс Рэдививус .

Короче говоря, общая проблема очень сложна.

Андрей использовал 80 строк кода поддержки, эти строки в свою очередь опирались на библиотеку Loki.

Cheers & hth,.

1 голос
/ 01 февраля 2011

Я выбираю шрифт большего размера:

Вспомогательные шаблоны:

template<bool b, typename A, typename B>
struct choose_if
{
    typedef A type;
};    
template<typename A, typename B>
struct choose_if<false, A, B>
{
    typedef B type;
};
template<typename A, typename B>
struct greater 
{
    static const bool value = sizeof(A) > sizeof(B);
    typedef vector<typename choose_if<value, A, B>::type> type;
};

Теперь используйте его:

template <typename A, typename B> 
typename greater<A, B>::type operator +(const vector<A> &a, const vector<B> &b) 
{ 
    typedef typename greater<A, B>::type  type;
    return type(a.x+b.x, a.y+b.y, a.z+b.z); 
}

См. Онлайн-демонстрацию: http://www.ideone.com/PGyA8

1 голос
/ 01 февраля 2011

Шаблон "Выбор типа" (читайте об этом в "Современном дизайне C ++") может быть полезен здесь.

template <bool flag, typename T, typename U>
struct Select {
    typedef T Result;
};

template <typename T, typename U>
struct Select<false, T, U> {
    typedef U Result;
};

...

template <typename A, typename B> 
vector<Select<sizeof(A) > sizeof(B), A, B>::Result> operator +(const vector<A> &a, const vector<B> &b) { 
   return vector<Select<sizeof(A) > sizeof(B), A, B>::Result>(a.x+b.x, a.y+b.y, a.z+b.z); 
}
1 голос
/ 01 февраля 2011

Существует относительно простой способ сделать это с помощью специализаций шаблонов

 template< typename A >
 struct TypePrecision {
    static const int precisionLevel;
 };

 template< typename A >
 const int TypePrecision< A >::precisionLevel = 0;


 template<>
 struct TypePrecision< float > {
    static const int precisionLevel;
 };
template<>
 struct TypePrecision< long float > {
    static const int precisionLevel;
 };
  template<>
 struct TypePrecision< double > {
    static const int precisionLevel;
 };
template<>
 struct TypePrecision< long double > {
    static const int precisionLevel;
 };

 template<>
 const int TypePrecision< float >::precisionLevel = 1;
 template<>
 const int TypePrecision< long float >::precisionLevel = 2;
 template<>
 const int TypePrecision< double >::precisionLevel = 3;
 template<>
 const int TypePrecision< long double >::precisionLevel = 4;

Затем вы используете это для создания HigherPrecisionType

 template < typename A , typename B >
 struct HigherPrecisionType
 { 
     static const int APrecision;
     static const int BPrecision;
 };

template < typename A , typename B >
const int HigherPrecisionType< A, B >::APrecision= TypePrecision< A >::precisionLevel;

template < typename A , typename B >
const int HigherPrecisionType< A, B >::BPrecision= TypePrecision< B >::precisionLevel;

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

0 голосов
/ 01 февраля 2011

Да. Вот метод C ++ 03:

template < typename T1, typename T2 >
struct which_return;

template < typename T >
struct which_return<T,T> { typedef std::vector<T> type; };

template <  >
struct which_return<int,double> { typedef std::vector<double> type; };

template < >
struct which_return<double,int> : which_return<int,double> {};

// etc...
template < typename T1, typename T2 >
typename which_return<T1,T2>::type operator+ (std::vector<T1> const&, std::vector<T2> const&)
{
  // ...
}

Очевидно, вы делаете это C ++ 0x, если можете.

0 голосов
/ 01 февраля 2011

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

template <typename A, typename B> 
vector<A> operator +(const vector<A> &a, const vector<B> &b) { 
   return vector<A>(a.x+b.x, a.y+b.y, a.z+b.z); 
}

вы также объявляете перегрузки для определенных типов, и они затем используются вместо общих:

vector<double> operator +(const vector<float> &a, const vector<double> &b) { 
   return vector<double>(a.x+b.x, a.y+b.y, a.z+b.z); 
}

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

0 голосов
/ 01 февраля 2011

Вы никогда не сможете достичь этого:

vector<float> + vector<double> would produce a vector<double>

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

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