惯用C ++ 11类型提升 [英] Idiomatic C++11 type promotion

查看:123
本文介绍了惯用C ++ 11类型提升的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于C ++的科学计算方面的出色论文,其中作者(T. Veldhuizen )建议使用一种基于特征的方法来提升类型.我已经使用了这种方法,并发现它是有效的:

There is a great paper on C++ for scientific computing where the author (T. Veldhuizen) suggests a traits-based approach to address type promotion. I have used such approach, and found it effective:

#include<iostream>
#include<complex>
#include<typeinfo>

template<typename T1, typename T2>
struct promote_trait{};

#define DECLARE_PROMOTION(A, B, C) template<> struct promote_trait<A, B> { using T_promote = C;};

DECLARE_PROMOTION(int, char, int);
DECLARE_PROMOTION(int, float, float);
DECLARE_PROMOTION(float, std::complex<float>, std::complex<float>);

// similarly for all possible type combinations...

template<typename T1, typename T2>
void product(T1 a, T2 b) {
  using T = typename promote_trait<T1, T2>::T_promote;
  T ans = T(a) * T(b);  
  std::cout<<"received "
           <<typeid(T1).name()<<"("<<a<<")"<<" * "
           <<typeid(T2).name()<<"("<<b<<")"<<" ==> "
           <<"returning "
           <<typeid(T).name()<<"("<<ans<<")"<<std::endl;
}

int main() {
  product(1, 'a');
  product(1, 2.0f);
  product(1.0f, std::complex<float>(1.0f, 2.0f));
  return 0;
}

输出:

received i(1) * c(a) ==> returning i(97)
received i(1) * f(2) ==> returning f(2)
received f(1) * St7complexIfE((1,2)) ==> returning St7complexIfE((1,2))

typeinfo返回的类型名称与实现有关;您的输出可能与我的输出不同,我的输出在OS X 10.7.4上使用了GCC 4.7.2

本质上,该方法定义了一个promote_trait,其中仅包含一个类型定义:以给定方式进行操作时,应将两种类型提升为该类型.需要宣布所有可能的晋升.

In essence, the approach defines a promote_trait which contains simply one type definition: the type to which two types should be promoted when operating in a given manner. One needs to declare all possible promotions.

当一个函数同时接收两种类型时,它依靠promote_trait推论出正确的,提升的结果类型.如果尚未为给定对定义特征,则代码将无法编译(理想功能).

When one function receives both types, it relies on the promote_trait to deduce the correct, promoted type of the result. If a trait has not been defined for a given pair the code fails to compile (a desirable feature).

现在,有问题的论文写于2000年,我们知道C ++在过去十年中发生了巨大的发展.那么,我的问题是:

Now, the paper in question was written in 2000, and we know that C++ has evolved dramatically over the past decade. My question, then, is the following:

是否存在像Veldhuizen引入的基于特征的方法一样有效的现代习惯C ++ 11方法来处理类型提升?

根据Luc Danton的建议,我创建了以下使用std::common_type的代码:

Based on Luc Danton's suggestion, I created the following code which uses std::common_type:

#include<iostream>
#include<complex>
#include<typeinfo>
#include<typeindex>
#include<string>
#include<utility>
#include<map>

// a map to homogenize the type names across platforms
std::map<std::type_index, std::string> type_names = {
  {typeid(char)                 , "char"},  
  {typeid(int)                  , "int"},
  {typeid(float)                , "float"},
  {typeid(double)               , "double"},
  {typeid(std::complex<int>)    , "complex<int>"},
  {typeid(std::complex<float>)  , "complex<float>"},
  {typeid(std::complex<double>) , "complex<double>"},
};

template<typename T1, typename T2>
void promotion(T1 a, T2 b) {
  std::string T1name = type_names[typeid(T1)];
  std::string T2name = type_names[typeid(T2)];
  std::string TPname = type_names[typeid(typename std::common_type<T1, T2>::type)];  
  std::cout<<T1name<<"("<<a<<") and "<<T2name<<"("<<b<<") promoted to "<<TPname<<std::endl;
}

int main() {
  promotion(1, 'a');
  promotion(1, 1.0);
  promotion(1.0, 1);
  promotion(std::complex<double>(1), 1);
  promotion(1.0f, 1);
  promotion(1.0f, 1.0);
  promotion(std::complex<int>(1), std::complex<double>(1));
  promotion(std::complex<double>(1), std::complex<int>(1));
  promotion(std::complex<float>(0, 2.0f), std::complex<int>(1));

  return 0;
}

输出:

int(1) and char(a) promoted to int
int(1) and double(1) promoted to double
double(1) and int(1) promoted to double
complex<double>((1,0)) and int(1) promoted to complex<double>
float(1) and int(1) promoted to float
float(1) and double(1) promoted to double
complex<int>((1,0)) and complex<double>((1,0)) promoted to complex<int>
complex<double>((1,0)) and complex<int>((1,0)) promoted to complex<int>
complex<float>((0,2)) and complex<int>((1,0)) promoted to complex<int>

我很惊讶地注意到,除了最后三个促销活动之外,其他所有活动都是我所期望的.为何将complex<int>complex<double>complex<float>提升为complex<int>!?

I am surprised to notice that all but the last three promotions are what I expected. Why would complex<int> and complex<double> or complex<float> be promoted to complex<int>!?

推荐答案

就像在catscradle答案中一样,decltypecommon_type(以及针对它的自定义专业化),可能是C ++ 11很好的替代品,可以满足转换的需要Veldhuizen要牢记的特质.但是,如果您仍然需要非常特定于将两种类型映射为一种(二进制运算符)的函数的求值,那么它仍将不足. (换句话说,decltype不知道问题的数学范围).

As in catscradle answer, decltype and common_type (and custom specializations for it), are probably good C++11 replacement for the need of conversion traits that Veldhuizen have in mind. However, it will still fall short if you need to be still very specific for the evaluation of a function that maps two types into one (binary operator). (In other words decltype doesn't know about the mathematical domain of your problem).

我的观点是,您可以诉诸Boost.MPL映射 http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html ,它甚至不需要C ++ 11,它是只是那个时候还没有写MPL:

My take is that you can resort to Boost.MPL maps http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html, this doesn't even require C++11, it is just that MPL wasn't written at that time:

#include<iostream>
#include<complex>
#include<typeinfo>
#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
// all traits in one place, no need for MACROS or C++11, compile error if the case does not exist.
using namespace boost::mpl;
typedef map<
    pair<pair<int, char>, int>,
    pair<pair<int, float>, int>,
    pair<pair<float, std::complex<float> >, std::complex<float> >
> mapped_promotion;

template<typename T1, typename T2>
void product(T1 a, T2 b) {
  typedef typename at<mapped_promotion, pair<T1, T2> >::type T;

  T ans = T(a) * T(b);  
  std::cout<<"received "
           <<typeid(T1).name()<<"("<<a<<")"<<" * "
           <<typeid(T2).name()<<"("<<b<<")"<<" ==> "
           <<"returning "
           <<typeid(T).name()<<"("<<ans<<")"<<std::endl;
}

int main() {
  product(1, 'a');
  product(1, 2.0f);
  product(1.0f, std::complex<float>(1.0f, 2.0f));
  return 0;
}

使用MPL的另一个额外好处是,您以后可以轻松地移至Boost.Fusion,一旦开始处理类型的代数",通常就是这种情况.而且没有什么可以替代C ++ 11核心语言中Boost.Fusion的功能.

Another extra benefit of using MPL is that you can then easily move to Boost.Fusion later, which usually is the case once you start dealing with "algebras" of types. And there is nothing to replace the functionality of Boost.Fusion in C++11 core language.

下面是一个更通用的解决方案,如果以上内容对您的应用程序来说足够了,您可以停止阅读,它结合了MPL和decltype并需要C ++ 11,允许未指定的类型对默认为decltype解决方案如果有什么好处,诀窍是看看mpl::map的返回是否为元类型void_(未找到对).

What follows is a more general solution, you can stop reading if the above was enough for your application, that combines MPL and decltype and requires C++11 which allows for unspecified pair of types to default to the decltype solution if it is any good, the trick is to see if the return of the mpl::map is the metatype void_ (pair not found).

...
#include <type_traits> 
//specific promotions
using namespace boost::mpl;
typedef map<
    pair<pair<int, char>, int>,
    pair<pair<int, float>, int>,
    pair<pair<float, std::complex<float> >, std::complex<float> >
> specific_mapped_promotion;

//promotion for unspecified combinations defaults to decltype type deduction.
template<class P1, class P2>
struct loose_mapped_promotion : std::conditional<
    std::is_same<typename at<specific_mapped_promotion, pair<P1, P2> >::type, mpl_::void_>::value,
    decltype( std::declval<P1>()*std::declval<P2>() ),
    typename at<specific_mapped_promotion, pair<P1, P2> >::type
> {};
template<typename T1, typename T2>
void product(T1 a, T2 b) {
  typedef typename loose_mapped_promotion<T1, T2>::type T;

  T ans = T(a) * T(b);
  ...
}
int main() {
   product(1.0, std::complex<double>(1.0f, 2.0f)); // now accepted, although no explicit trait was made
}

最后一点:如果要使用std::common_type,在特殊情况下重载显然是可以的: http://www.cplusplus.com/reference/type_traits/common_type/

On a final note: it is apparently ok to overload std::common_type for special cases, if you want to use it: http://www.cplusplus.com/reference/type_traits/common_type/

这篇关于惯用C ++ 11类型提升的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆