Использование заголовка продвижения арифметики C++

Я играл вокруг с рядом шаблонов для определения, что корректное продвижение вводит, учитывая два типов примитивов в C++. Идея состоит в том, что при определении пользовательского числового шаблона Вы могли бы использовать их для определения типа возврата, скажем, оператора +, функция на основе класса передала шаблонам. Например:

// Custom numeric class
template <class T>
struct Complex {
    Complex(T real, T imag) : r(real), i(imag) {}
    T r, i;
// Other implementation stuff
};

// Generic arithmetic promotion template
template <class T, class U>
struct ArithmeticPromotion {
    typedef typename X type;  // I realize this is incorrect, but the point is it would
                              // figure out what X would be via trait testing, etc
};

// Specialization of arithmetic promotion template
template <>
class ArithmeticPromotion<long long, unsigned long> {
    typedef typename unsigned long long type;
}

// Arithmetic promotion template actually being used
template <class T, class U>
Complex<typename ArithmeticPromotion<T, U>::type>
operator+ (Complex<T>& lhs, Complex<U>& rhs) {
    return Complex<typename ArithmeticPromotion<T, U>::type>(lhs.r + rhs.r, lhs.i + rhs.i);
}

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

Кстати, Повышение имеет что-то подобное в своем математическом/инструменте/продвижении заголовке, но это действительно больше для того, чтобы подготовить значения, которые будут переданы стандарту C математические функции (которые ожидают, что или 2 ints или 2 удваиваются), и обходит все целочисленные типы. Что-то то, что простой предпочтительный для того, чтобы иметь полный контроль над тем, как Ваши объекты преобразовываются?

TL; DR: Какие виды шаблонов помощника Вы ожидали бы находить в арифметическом заголовке продвижения вне оборудования, которое делает само продвижение?

7
задан BenTrofatter 11 March 2010 в 15:41
поделиться

2 ответа

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

template<typename Atype, typename Btype>
type_promote<Atype, Btype>::type operator+(Atype A, Btype B);

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

Что касается вопроса о том, что с ними должно быть: я только что проверил наш исходный код, где мы их определяем, и все, что у нас есть, это просто описанное вами простое объявление ArithmeticPromotion - три общие версии для решения сложных проблем. сложные, комплексно-реальные и действительно-сложные варианты с использованием конкретных реальных и реальных вариантов, а затем список реальных и реальных вариантов - всего около 50 строк кода. У нас нет никаких других вспомогательных шаблонов с ними, и (судя по нашему использованию) не похоже, что есть какие-то естественные, которые мы бы использовали.

(FWIW, если вы не хотите писать это самостоятельно, загрузите наш исходный код с http://www.codesourcery.com/vsiplplusplus/2.2/download.html и вытащите ] src / vsip / core / promotion.hpp . Это даже в той части нашей библиотеки, которая лицензирована BSD, хотя на самом деле об этом не говорится в самом файле.)

2
ответ дан 6 December 2019 в 15:20
поделиться

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

Вам нужно иметь особый случай для непродвинутых типов char, short и их беззнаковых/знаковых версий, поскольку при применении к двум таким операндам разных типов, результатом не будет ни один из них. Также необходимо позаботиться о случае, когда два класса могут быть преобразованы в раскрученные арифметические типы. Чтобы разобраться с этим, мы проверяем, является ли результат ?: раскрученным арифметическим типом (в духе пункта 13.6), и используем этот тип.

// typedef eiher to A or B, depending on what integer is passed
template<int, typename A, typename B>
struct cond;

#define CCASE(N, typed) \
  template<typename A, typename B> \
  struct cond<N, A, B> { \
    typedef typed type; \
  }

CCASE(1, A); CCASE(2, B);
CCASE(3, int); CCASE(4, unsigned int);
CCASE(5, long); CCASE(6, unsigned long);
CCASE(7, float); CCASE(8, double);
CCASE(9, long double);

#undef CCASE

// for a better syntax...
template<typename T> struct identity { typedef T type; };

// different type => figure out common type
template<typename A, typename B>
struct promote {
private:
  static A a;
  static B b;

  // in case A or B is a promoted arithmetic type, the template
  // will make it less preferred than the nontemplates below
  template<typename T>
  static identity<char[1]>::type &check(A, T);
  template<typename T>
  static identity<char[2]>::type &check(B, T);

  // "promoted arithmetic types"
  static identity<char[3]>::type &check(int, int);
  static identity<char[4]>::type &check(unsigned int, int);
  static identity<char[5]>::type &check(long, int);
  static identity<char[6]>::type &check(unsigned long, int);
  static identity<char[7]>::type &check(float, int);
  static identity<char[8]>::type &check(double, int);
  static identity<char[9]>::type &check(long double, int);

public:
  typedef typename cond<sizeof check(0 ? a : b, 0), A, B>::type
    type;
};

// same type => finished
template<typename A>
struct promote<A, A> {
  typedef A type;
};

Если ваши типы Complex могут быть преобразованы друг в друга, то ?: не найдет общего типа. Вы можете специализировать promote, чтобы указать ему, как найти общий тип двух Complex:

template<typename T, typename U>
struct promote<Complex<T>, Complex<U>> {
  typedef Complex<typename promote<T, U>::type> type;
};

Использование простое:

int main() {
  promote<char, short>::type a;
  int *p0 = &a;

  promote<float, double>::type b;
  double *p1 = &b;

  promote<char*, string>::type c;
  string *p2 = &c;
}

Обратите внимание, что для реального использования лучше всего уловить несколько случаев, которые я опустил для простоты, например, следует обрабатывать аналогично (лучше всего сначала снять const и volatile и преобразовать T[N] в T* и T& в T, а затем делегировать к настоящему шаблону promote - i. т.е. сделать boost::remove_cv>::type для обоих A и B перед их делегированием). Если этого не сделать, то вызов check закончится неоднозначностью для таких случаев.

12
ответ дан 6 December 2019 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: