【问题标题】:Uses of a C++ Arithmetic Promotion HeaderC++ 算术提升标头的使用
【发布时间】:2011-01-26 10:44:51
【问题描述】:

我一直在使用一组模板来确定在 C++ 中给定两种基本类型的正确促销类型。这个想法是,如果您定义了一个自定义数字模板,您可以使用它们来确定返回类型,例如,基于传递给模板的类的 operator+ 函数。例如:

// 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);
}

如果您使用这些促销模板,您可以或多或少地将您的用户定义类型视为原语,并应用相同的促销规则。所以,我想我的问题是这会有用吗?如果是这样,您希望模板化哪些常见任务以便于使用?我的工作是假设仅拥有促销模板不足以实际采用。

顺便说一下,Boost 在它的 math/tools/promotion 标头中有类似的东西,但它实际上更多是为了让值准备好传递给标准 C 数学函数(期望 2 个整数或 2 个双精度数)并绕过所有整数类型。比完全控制对象的转换方式更简单吗?

TL;DR:除了执行提升本身的机制之外,您希望在算术提升标头中找到哪些类型的帮助模板?

【问题讨论】:

  • 我想在最后你将把提升良好的结果分配给一些变量(不同类型的),所以我看不出它有多少实际用途。也许在 C++0x 中使用 auto,但我也认为 decltype 可以更轻松地完成大部分工作。
  • 事实证明,这将在 C++0x 中作为 CommonType 结构实现,它利用 decltype 来找出正确的类型。如果有人想要完全统一的行为与标准关于提升/转换的规则有关,他们只需要编写模板的可交换变体。

标签: c++ math templates boost


【解决方案1】:

为此,您可以使用?: 运算符。它将为您提供两种类型之间的通用类型。首先,如果这两种类型相同,则可以。然后,如果类型不同,则调用 ?: 并查看返回的类型。

您需要对非提升类型charshort 及其无符号/有符号版本进行特殊处理,因为应用于两个不同类型的此类操作数,结果将不是它们。您还需要注意两个类可以转换为提升的算术类型的情况。为了解决这些问题,我们检查?: 的结果是否是提升的算术类型(本着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&lt;T&gt; 类型可以相互转换,?: 将找不到通用类型。你可以专门化promote 告诉它如何找出两个Complex&lt;T&gt; 的共同类型:

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;
}

请注意,对于实际使用,您最好抓住一些我为简单起见而遗漏的情况,例如 &lt;const int, int&gt; 的处理方式应类似于 &lt;T, T&gt;(您最好先去掉 constvolatile 和将T[N] 转换为T*,将T&amp; 转换为T,然后委托给实际的promote 模板——即在委托之前对AB 执行boost::remove_cv&lt;boost::decay&lt;T&gt;&gt;::type)。如果您不这样做,对check 的调用将在这些情况下变得模棱两可。

【讨论】:

    【解决方案2】:

    这绝对是有用的——我们在我工作的数学库中使用这些东西来正确地在表达式中键入中间值。例如,您可能有一个模板化的加法运算符:

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

    这样,您可以编写一个通用运算符来处理不同的参数类型,它会返回一个适当类型的值,以避免它出现在表达式中的精度损失。它也很有用(在向量和之类的事情中) ) 用于在这些运算符中正确声明内部变量。

    至于这些应该如何处理的问题:我刚刚检查了我们定义它们的源代码,我们所拥有的只是您描述的简单 ArithmeticPromotion 声明——三个通用版本来解决复杂问题—— complex、complex-real 和 real-complex 变体使用特定的 real-real 变体,然后是 real-real 的列表——总共大约 50 行代码。我们没有任何其他帮助模板,而且(从我们的使用情况来看)看起来没有我们会使用的任何自然模板。

    (FWIW,如果你不想自己写,请从http://www.codesourcery.com/vsiplplusplus/2.2/download.html 下载我们的源代码,然后拉出src/vsip/core/promote.hpp。这甚至在我们的库中获得 BSD 许可的部分,尽管它没有实际上在文件本身中是这样说的。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-01
      • 2020-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-27
      相关资源
      最近更新 更多