2013-05-31 6 views
12

वैज्ञानिक कंप्यूटिंग के लिए सी ++ पर a great paper है जहां लेखक (टी। वेल्डुइज़ेन) लक्षण-आधारित पता प्रकार पदोन्नति के लिए दृष्टिकोण सुझाता है। मैं इस तरह के दृष्टिकोण का इस्तेमाल किया है, और यह प्रभावी पाया: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)) 

प्रकार typeinfo द्वारा लौटाए के नाम कार्यान्वयन निर्भर है; अपने उत्पादन मेरा है, जो ओएस पर जीसीसी 4.7.2 इस्तेमाल किया से अलग हो सकता एक्स 10.7.4

संक्षेप में, दृष्टिकोण एक promote_trait जो केवल एक प्रकार परिभाषा में शामिल है परिभाषित करता है: प्रकार है जो दो प्रकार जब ऑपरेटिंग बढ़ावा दिया जाना चाहिए करने के लिए एक दिए गए तरीके से। किसी को सभी संभावित प्रचार घोषित करने की आवश्यकता है।

एक समारोह दोनों प्रकार प्राप्त करता है, यह promote_trait पर निर्भर करता है सही, परिणाम की पदोन्नत प्रकार निकालना। यदि किसी दिए गए जोड़ी के लिए कोई विशेषता परिभाषित नहीं की गई है तो कोड संकलित करने में विफल रहता है (एक वांछनीय विशेषता)।

अब प्रश्न में पेपर 2000 में लिखा गया था, और हम जानते हैं कि सी ++ पिछले दशक में नाटकीय रूप से विकसित हुआ है।

वहाँ लक्षण आधारित दृष्टिकोण Veldhuizen द्वारा शुरू के रूप में प्रभावी प्रकार पदोन्नति के साथ काम करने के लिए एक आधुनिक, मुहावरेदार सी ++ 11 दृष्टिकोण है: मेरा प्रश्न है, तो, निम्नलिखित है?

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

मैं हैरान हूं

(std::common_type के प्रयोग पर) संपादन

ल्यूक डेंटन के सुझाव के आधार पर, मैं निम्नलिखित कोड जो std::common_type का उपयोग करता है बनाया यह ध्यान देने के लिए कि पिछले तीन प्रचारों के अलावा सभी की अपेक्षा है। complex<int> और complex<double> या complex<float> को complex<int> पर प्रचारित क्यों किया जाएगा!

+3

आप 'std :: common_type' से परिचित हैं? –

+0

@ ल्यूकडैंटन: बहुत, बहुत उपयोगी। मैं हैरान हूं, तथापि, 'std :: जटिल ' और 'std :: जटिल ' और 'std :: जटिल ' के बीच प्रकार पदोन्नति के बारे में। कोई अंतर्दृष्टि? – Escualo

+1

यह शायद 'std :: complex' के प्राथमिक टेम्पलेट की वजह से है, और की बात:", डबल या लंबी डबल नाव के अलावा अन्य, किसी भी प्रकार के लिए टेम्पलेट जटिल instantiating के प्रभाव अनिर्दिष्ट है। " –

उत्तर

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); के लिए आप सिर्फ auto उपयोग कर सकते हैं, auto ans = T(a) * T(b);

+0

यह पूरी तरह से ओपी के बराबर नहीं है। – Puppy

+0

@DeadMG, मैं सहमत हूं, इसमें 'promotion_traits' के लिए कुछ व्यावहारिक आवश्यकताएं शामिल हैं लेकिन यह एक प्रतिस्थापन नहीं है। – alfC

+0

@DeadMG: नहीं, यह बराबर नहीं है। यह बेहतर है। दो पॉइंटर्स के बीच का अंतर एक पूर्णांक नहीं है जो एक सूचक है। और कई अन्य मामले जहां एक 'common_type' जैसी विधि विफल हो जाती है। –

4

catscradle में के रूप में उत्तर, decltype और common_type (और इसके लिए कस्टम स्पेशलाइजेशन), शायद वेल्डुइज़ेन के दिमाग में रूपांतरण गुणों की आवश्यकता के लिए अच्छा सी ++ 11 प्रतिस्थापन है। हालांकि, यदि आपको दो प्रकारों में एक (बाइनरी ऑपरेटर) मानचित्रित करने वाले फ़ंक्शन के मूल्यांकन के लिए विशिष्ट बहुत होना आवश्यक है, तो यह अभी भी कम हो जाएगा। (दूसरे शब्दों में decltype आपकी समस्या के गणितीय डोमेन के बारे में नहीं जानता है)।

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

एक और:

मेरे ले कि आप सहारा कर सकते हैं Boost.MPL नक्शे को http://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual/map.html, यह और भी सी ++ 11 की आवश्यकता नहीं है, यह सिर्फ है कि एमपीएल उस समय नहीं लिखा गया था है एमपीएल का उपयोग करने का अतिरिक्त लाभ यह है कि आप आसानी से Boost.Fusion पर आसानी से स्थानांतरित कर सकते हैं, जो एक बार जब आप "बीजगणित" प्रकारों से निपटना शुरू करते हैं तो यह मामला है। और C++ 11 कोर भाषा में Boost.Fusion की कार्यक्षमता को प्रतिस्थापित करने के लिए कुछ भी नहीं है।

क्या इस प्रकार एक अधिक सामान्य समाधान, आप पढ़ अगर ऊपर अपने आवेदन, कि एमपीएल को जोड़ती है और decltype और की आवश्यकता के लिए पर्याप्त था रोक सकता है सी ++ 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 
} 

एक अंतिम टिप्पणी पर: यह जाहिरा तौर पर विशेष मामलों के लिए std::common_type ओवरलोड के लिए ठीक है, आप इसे उपयोग करना चाहते हैं: http://www.cplusplus.com/reference/type_traits/common_type/

+0

मैं इसे देख लूंगा - धन्यवाद! – Escualo

संबंधित मुद्दे