2013-05-31 105 views
12

在科學計算的C++上有a great paper作者(T. Veldhuizen)建議基於特徵的處理地址類型提升的方法。我用這樣的方法,並發現它有效:Idiomatic C++ 11類型促銷

#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)) 

由所屬類別返回類型的名稱是依賴於實現的;您的輸出可從礦,它使用GCC 4.7.2在OS不同X 10.7.4

在本質上,該方法定義了一個promote_trait它簡單地含有一種類型定義:類型,其中兩個類型應該進行操作時,推動以給定的方式。我們需要宣佈所有可能的促銷活動。

當一個函數接收到這兩種類型時,它依靠promote_trait來推導出結果的正確提升類型。如果某特定配對沒有定義特徵,則代碼無法編譯(一個理想特徵)。

現在,這篇論文是在2000年編寫的,我們知道C++在過去的十年裏有了很大的發展。那麼,我的問題如下:

有沒有一種現代的,習慣的C++ 11方法來處理類型提升與Veldhuizen引入的基於特徵的方法一樣有效?

編輯(使用std::common_type

基於呂克丹東的建議下,我創建了下面的代碼,它使用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>!?

+3

你熟悉'的std :: common_type'? –

+0

@LucDanton:非常非常有用。然而,我很驚訝'std :: complex '和'std :: complex '和'std :: complex '之間的類型提升。任何見解? – Escualo

+1

這可能是由於'std :: complex'的主模板,並且說到:「實例化除float,double或long double之外的任何其他類型的模板複合的效果未指定。」 –

回答

3

我認爲你可以使用decltype此:

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

或者與declval

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

編輯對於T ans = T(a) * T(b);你可以使用autoauto ans = T(a) * T(b);

+0

這完全不等同於OP的內容。 – Puppy

+0

@DeadMG,我同意,它涵蓋'promote_traits'的一些實際需要,但它不是替代品。 – alfC

+0

@DeadMG:不,它不相同。這更好。兩個指針之間的差異是一個不是指針的整數。還有很多其他類似'common_type'的方法失敗。 –

4

正如catscradle回答,decltypecommon_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; 
} 

另一個使用MPL的額外好處是您可以稍後輕鬆移動到Boost.Fusion,一旦您開始處理類型的「代數」,ussualy就是這種情況。而且沒有什麼可以替代C++ 11核心語言中Boost.Fusion的功能。

接下來是一個更一般的解決方案,如果上面的代碼足以滿足您的應用程序需求,它將MPL和decltype結合起來,並且要求C++ 11允許未指定的一對類型默認爲decltype解決方案if這是好的,訣竅是看看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 
} 

最後要注意:這顯然是確定超載std::common_type特殊情況下,如果你想使用它:http://www.cplusplus.com/reference/type_traits/common_type/

+0

我會檢查出來的 - 謝謝! – Escualo