惯用语C++ 11类型推广

Esc*_*alo 14 c++ c++11 type-promotion

关于科学计算的C++ 有一篇很好的论文,其中作者(T. Veldhuizen)提出了一种基于特征的方法来解决类型推广问题.我使用过这种方法,发现它有效:

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

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))
Run Code Online (Sandbox Code Playgroud)

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

本质上,该方法定义了一个promote_trait只包含一个类型定义的类型:在以给定方式操作时应该提升两种类型的类型.需要宣布所有可能的促销活动.

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

现在,有关论文写于2000年,我们知道C++在过去十年中发展迅速.那么,我的问题如下:

是否有一种现代的,惯用的C++ 11方法来处理类型提升与Veldhuizen引入的基于特征的方法一样有效?

编辑(使用时std::common_type)

根据Luc Danton的建议,我创建了以下代码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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

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>
Run Code Online (Sandbox Code Playgroud)

我很惊讶地注意到除了最后三次促销之外的所有促销活动都是我的预期.为什么会complex<int>complex<double>complex<float>晋升为complex<int>!?

alf*_*lfC 5

正如catscradle的答案,decltype以及common_type(以及它的自定义特化),可能是很好的C++ 11替代Veldhuizen想到的转换特性的需要.但是,如果您仍需要非常具体地评估将两种类型映射为一种(二元运算符)的函数,它仍然会失败.(换句话说decltype,不知道你的问题的数学领域).

我的看法是你可以使用Boost.MPL地图http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html,这甚至不需要C++ 11 ,那只是MPL当时没有写的:

#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;
}
Run Code Online (Sandbox Code Playgroud)

使用MPL的另一个好处是,您可以轻松地移动到Boost.Fusion以后,一旦您开始处理类型的"代数",通常就是这种情况.在C++ 11核心语言中没有任何东西可以取代Boost.Fusion的功能.


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

...
#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
}
Run Code Online (Sandbox Code Playgroud)

最后一点:std::common_type如果您想使用它,显然可以在特殊情况下超载:http://www.cplusplus.com/reference/type_traits/common_type/


cat*_*dle 3

我想你可以用decltype这个:

template <typename T, typename U>
void product(T t, U u) 
{
    std::cout << typeid(decltype(t * u)).name() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

或者与declval

#include <utility>
template <typename T, typename U>
void product() 
{
    std::cout << typeid(decltype(std::declval<T>() * std::declval<U>())).name() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

编辑T ans = T(a) * T(b);可以只使用autoauto ans = T(a) * T(b);