2012-08-04 12 views
35
अभिव्यक्ति टेम्पलेट्स में से एक विशेष लाभ पर

आइए नज़र: टिकट स्मृति में वेक्टर आकार temporaries जो की तरह अतिभारित ऑपरेटरों में पाए जाते हैं से बचने के लिए इस्तेमाल किया जा सकता:अभिव्यक्ति टेम्पलेट्स और सी ++ 11

template<typename T> 
std::vector<T> operator+(const std::vector<T>& a, const std::vector<T>& b) 
{ 
    std::vector<T> tmp; // vector-sized temporary 
    for_each(...); 
    return tmp; 
} 

C++ में 11 इस समारोह का रिटर्न स्टेटमेंट चाल semantics लागू होता है। वेक्टर की कोई प्रति नहीं। यह एक जीत है।

हालांकि, अगर मैं की तरह

d = a + b + c; 

एक सरल अभिव्यक्ति को देखो मुझे लगता है कि इसके बाद के संस्करण समारोह में दो बार फोन किया जाता है (दोनों operator+ के लिए) जबकि अंतिम असाइनमेंट चाल अर्थ विज्ञान के साथ किया जा सकता है।

कुल 2 लूप निष्पादित किए जाते हैं। इसका मतलब है कि मैंने एक अस्थायी रूप से बाहर रखा और इसे बाद में वापस पढ़ा। बड़े वैक्टरों के लिए यह कैश से बाहर आता है। यह अभिव्यक्ति टेम्पलेट्स से भी बदतर है। वे पूरी चीज सिर्फ 1 पाश में कर सकते हैं।

for(int i=0 ; i < vec_length ; ++i) 
    d[i] = a[i] + b[i] + c[i]; 

मैं सोच रहा था कि क्या इस कदम अर्थ विज्ञान या किसी अन्य नई सुविधा के साथ lambdas एक साथ टिकट के रूप में रूप में अच्छा कर सकते हैं: टिकट उपरोक्त कोड बराबर करने के लिए निष्पादित कर सकते हैं। कोई विचार?

संपादित करें:

मूल रूप से, एट तकनीक का उपयोग कर संकलक एक पार्स पेड़ कि बीजीय अभिव्यक्ति जैसा दिखता है के साथ यह प्रकार प्रणाली है बनाता है। इस पेड़ में आंतरिक नोड्स और पत्ता नोड होते हैं। आंतरिक नोड्स संचालन (जोड़, गुणा, आदि) का प्रतिनिधित्व करते हैं और पत्ती नोड डेटा ऑब्जेक्ट्स के संदर्भ का प्रतिनिधित्व करते हैं।

मैंने स्टैक मशीन के फैशन में पूरी गणना प्रक्रिया के बारे में सोचने की कोशिश की: ऑपरेशन स्टैक से ऑपरेशन करें और तर्क स्टैक से अगले तर्क और ऑपरेशन का मूल्यांकन करें। परिणाम को ऑपरेशन के लिए इंतजार कर रहे स्टैक पर वापस रखें।

इन दो अलग अलग वस्तुओं (आपरेशन ढेर और डेटा पत्ती ढेर) मैं कार्यों के लिए एक std::tuple और एक std::tuple एक साथ बंडल के लिए डेटा एक std::pair<> में छोड़ देता है प्रतिनिधित्व करने के लिए। प्रारंभ में मैं ने std:vector का उपयोग किया लेकिन इसके परिणामस्वरूप रनटाइम ओवरहेड हुआ।

पूरी प्रक्रिया दो चरणों में जाती है: स्टैक मशीन प्रारंभिकरण जहां ऑपरेशन और तर्क स्टैक प्रारंभ किया गया है। और मूल्यांकन चरण जो वेक्टर में जोड़े गए कंटेनर असाइन करके ट्रिगर किया जाता है।

मैं एक वर्ग Vec जो एक निजी array<int,5> ( पेलोड) रखती है और जो एक ओवरलोड असाइनमेंट ऑपरेटर कि "अभिव्यक्ति" दिखाती है बनाया।

वैश्विक operator* Vec और "अभिव्यक्ति" लेने जहां हम सिर्फ a*b से अधिक सही से निपटने के भी मामले में सक्षम करने के लिए के सभी संयोजनों के लिए ओवरलोड हो गया है।(सूचना, मैं गुणा करने के लिए इस शैक्षिक उदाहरण के लिए बंद - मूल रूप से जल्दी से पहचानना कोडांतरक में imull।)

क्या मूल्यांकन करने से पहले किया जाता है शुरू होता है है शामिल Vec से बाहर मूल्यों "को निकालने" ऑब्जेक्ट्स और तर्क स्टैक प्रारंभ करना। यह आवश्यक था कि के आसपास विभिन्न प्रकार की वस्तुएं न हों: इंडेक्स करने योग्य वैक्टर और गैर-अनुक्रमणीय परिणाम। यह Extractor है। अच्छी बात फिर से: वैराडिक टेम्पलेट्स का उपयोग किया जाता है जो इस मामले में कोई रन-टाइम ओवरहेड नहीं होता है (यह सब संकलन समय पर किया जाता है)।

पूरी बात काम करती है। अभिव्यक्ति का अच्छी तरह से मूल्यांकन किया गया है (मैं भी जोड़ा जोड़ा गया है, लेकिन कोड को फिट करने के लिए यहां छोड़ा गया है)। के नीचे आप असेंबलर आउटपुट देख सकते हैं। बस कच्चे compuation, ठीक है जैसे आप यह होना चाहते हैं: ईटी तकनीक के साथ एन-पैरा।

उपशॉट। सी ++ 11 की नई भाषा विशेषताएं विविधता टेम्पलेट्स प्रदान करती हैं जो (टेम्पलेट मेटा-प्रोग्रामिंग के साथ) संकलन समय गणना के क्षेत्र को खोलती हैं। मैंने यहां दिखाया कि कैसे विविधता टेम्पलेट्स का लाभ पारंपरिक ईटी तकनीक के साथ कोड के उत्पादन के लिए उपयोग किया जा सकता है।

#include<algorithm> 
#include<iostream> 
#include<vector> 
#include<tuple> 
#include<utility> 
#include<array> 



template<typename Target,typename Tuple, int N, bool end> 
struct Extractor { 
    template < typename ... Args > 
    static Target index(int i,const Tuple& t, Args && ... args) 
    { 
    return Extractor<Target, Tuple, N+1, 
      std::tuple_size<Tuple>::value == N+1>:: 
     index(i, t , std::forward<Args>(args)..., std::get<N>(t).vec[i]); 
    } 
}; 

template < typename Target, typename Tuple, int N > 
struct Extractor<Target,Tuple,N,true> 
{ 
    template < typename ... Args > 
    static Target index(int i,Tuple const& t, 
      Args && ... args) { 
     return Target(std::forward<Args>(args)...); } 
}; 

template < typename ... Vs > 
std::tuple<typename std::remove_reference<Vs>::type::type_t...> 
extract(int i , const std::tuple<Vs...>& tpl) 
{ 
    return Extractor<std::tuple<typename std::remove_reference<Vs>::type::type_t...>, 
      std::tuple<Vs...>, 0, 
      std::tuple_size<std::tuple<Vs...> >::value == 0>::index(i,tpl); 
} 


struct Vec { 
    std::array<int,5> vec; 
    typedef int type_t; 

    template<typename... OPs,typename... VALs> 
    Vec& operator=(const std::pair< std::tuple<VALs...> , std::tuple<OPs...> >& e) { 
    for(int i = 0 ; i < vec.size() ; ++i) { 
     vec[i] = eval(extract(i,e.first) , e.second); 
    } 
    } 
}; 




template<int OpPos,int ValPos, bool end> 
struct StackMachine { 
    template<typename... OPs,typename... VALs> 
    static void eval_pos(std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops) 
    { 
    std::get<ValPos+1>(vals) = 
     std::get<OpPos>(ops).apply(std::get<ValPos>(vals) , 
        std::get<ValPos+1>(vals)); 
    StackMachine<OpPos+1,ValPos+1,sizeof...(OPs) == OpPos+1>::eval_pos(vals,ops); 
    } 
}; 

template<int OpPos,int ValPos> 
struct StackMachine<OpPos,ValPos,true> { 
    template<typename... OPs,typename... VALs> 
    static void eval_pos(std::tuple<VALs...>& vals , 
      const std::tuple<OPs...> & ops) 
    {} 
}; 



template<typename... OPs,typename... VALs> 
int eval(const std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops) 
{ 
    StackMachine<0,0,false>::eval_pos(const_cast<std::tuple<VALs...>&>(vals),ops); 
    return std::get<sizeof...(OPs)>(vals); 
} 




struct OpMul { 
    static int apply(const int& lhs,const int& rhs) { 
    return lhs*rhs; 
    } 
}; 

std::pair< std::tuple< const Vec&, const Vec& > , std::tuple<OpMul> > 
operator*(const Vec& lhs,const Vec& rhs) 
{ 
    return std::make_pair(std::tuple< const Vec&, const Vec& >(lhs , rhs) , 
      std::tuple<OpMul>(OpMul())); 
} 

template<typename... OPs,typename... VALs> 
std::pair< std::tuple< const Vec&, VALs... > , std::tuple<OPs...,OpMul> > 
operator*(const Vec& lhs,const std::pair< std::tuple<VALs...> , std::tuple<OPs...> >& rhs) 
{ 
    return std::make_pair(std::tuple_cat(rhs.first , std::tuple< const Vec& >(lhs) ) , 
      std::tuple_cat(rhs.second , std::tuple<OpMul>(OpMul()) )); 
} 

template<typename... OPs,typename... VALs> 
std::pair< std::tuple< const Vec&, VALs... > , std::tuple<OPs...,OpMul> > 
operator*(const std::pair< std::tuple<VALs...> , std::tuple<OPs...> >& lhs, 
     const Vec& rhs) 
{ 
    return std::make_pair(std::tuple_cat(lhs.first , std::tuple< const Vec& >(rhs) ) , 
      std::tuple_cat(lhs.second , std::tuple<OpMul>(OpMul()))); 
} 

int main() 
{ 
    Vec d,c,b,a; 


    for(int i = 0 ; i < d.vec.size() ; ++i) { 
    a.vec[i] = 10+i; 
    b.vec[i] = 20+i; 
    c.vec[i] = 30+i; 
    d.vec[i] = 0; 
    } 

    d = a * b * c * a; 

    for(int i = 0 ; i < d.vec.size() ; ++i) 
    std::cout << d.vec[i] << std::endl; 
} 

असेंबलर g++-4.6 -O3 साथ उत्पन्न (मैं वेक्टर प्रारंभ में कुछ क्रम निर्भरता डाल करने के लिए इतना है कि संकलक संकलन समय पर पूरी बात की गणना नहीं करता है और आप वास्तव में imull instaructions देखने के लिए किया था।)

imull %esi, %edx 
imull 32(%rsp), %edx 
imull %edx, %esi 
movl 68(%rsp), %edx 
imull %ecx, %edx 
movl %esi, (%rsp) 
imull 36(%rsp), %edx 
imull %ecx, %edx 
movl 104(%rsp), %ecx 
movl %edx, 4(%rsp) 
movl 72(%rsp), %edx 
imull %ecx, %edx 
imull 40(%rsp), %edx 
imull %ecx, %edx 
movl 108(%rsp), %ecx 
movl %edx, 8(%rsp) 
movl 76(%rsp), %edx 
imull %ecx, %edx 
imull 44(%rsp), %edx 
imull %ecx, %edx 
movl 112(%rsp), %ecx 
movl %edx, 12(%rsp) 
movl 80(%rsp), %edx 
imull %ecx, %edx 
imull %eax, %edx 
imull %ecx, %edx 
movl %edx, 16(%rsp) 
+3

आपको [कॉपी एलिजन और आरवीओ] (http://en.wikipedia.org/wiki/Copy_elision) देखना चाहिए। साथ ही, अपनी खुद की 'tmp' प्रतिलिपि बनाने के बजाय मूल्यों में से एक को पास करने से मदद मिल सकती है। – juanchopanza

+0

मूल्य से गुजरने से मदद नहीं मिल सकती है (एक प्रतिलिपि अभी भी बनाई गई है)। (एन) आरवीओ अतिरिक्त लूप – ritter

+0

@ फ्रैंक को खत्म करने में मदद नहीं करता है: क्या आप निश्चित हैं? यह आरवीओ के लिए एक मजबूत उम्मीदवार है। – akappa

उत्तर

45

मैं सोच रहा था कि लेम्बास एक साथ चलने वाले अर्थशास्त्र या किसी अन्य नई सुविधा के साथ ईटीएस के रूप में अच्छा कर सकता है या नहीं। कोई विचार?

त्वरित उत्तर

ले जाएँ अर्थ विज्ञान ऐसी अभिव्यक्ति टेम्पलेट्स (ETS) के रूप में अपने स्वयं के --techniques अभी भी जरूरत है सी ++ 11 पर कुल रामबाण इस तरह के आसपास डेटा ले जाने के रूप में ऊपरी खर्चों को खत्म करने की नहीं हैं ! तो, मेरे बाकी जवाब में डाइविंग से पहले अपने प्रश्न का उत्तर देने के लिए, अर्थशास्त्र को स्थानांतरित करें, आदि ईटी को पूरी तरह से प्रतिस्थापित नहीं करता है क्योंकि मेरा जवाब नीचे दिखाता है।

विस्तृत जवाब

टिकट आम तौर पर बाद में जब तक मूल्यांकन स्थगित करने प्रॉक्सी वस्तुओं वापसी, इसलिए तत्काल कोई स्पष्ट लाभ कोड है कि गणना से चलाता है जब तक सी ++ 11 भाषा सुविधाओं के नहीं है। उस ने कहा, कोई ईटी कोड लिखना नहीं चाहेगा, हालांकि, प्रॉक्सी के साथ अभिव्यक्ति वृक्ष के निर्माण के दौरान रन-टाइम कोड पीढ़ी को ट्रिगर करता है। अच्छी तरह से, सी ++ 11 की चाल सेमेटिक्स और सही अग्रेषण इस तरह के ओवरहेड से बचने में मदद कर सकता है जो अन्यथा होता है। (सी ++ 03 में ऐसा संभव नहीं होता।)

अनिवार्य रूप से, जब ईटी लिखते हैं तो एक बार शामिल होने वाली प्रॉक्सी ऑब्जेक्ट्स के सदस्य फ़ंक्शन (ओं) इष्टतम कोड उत्पन्न करने के तरीके में भाषा सुविधाओं का फायदा उठाना चाहते हैं सक्रिय किया गया। सी ++ 11 में इसमें पूर्ण अग्रेषण का उपयोग करना शामिल होगा, प्रतिलिपि बनाने पर अर्थशास्त्र को स्थानांतरित करना होगा। यदि वास्तव में अभी भी इसके ऊपर और ऊपर की आवश्यकता है जो संकलक पहले से ही कर सकता है।गेम का नाम रन-टाइम कोड को कम करने और/या रन-टाइम गति को अधिकतम करने और/या रन-टाइम ओवरहेड को कम करने के लिए है।

मैं वास्तव में सी ++ 11 सुविधाओं के साथ कुछ ईटीएस को आजमाने की कोशिश करता हूं यह देखने के लिए कि क्या मैं a = b + c + d; अभिव्यक्ति के साथ सभी मध्यवर्ती अस्थायी उदाहरण प्रकारों को बढ़ा सकता हूं। (चूंकि यह मेरी सामान्य गतिविधियों से सिर्फ एक मजेदार ब्रेक था इसलिए मैंने इसकी तुलना नहीं की थी या सी ++ 03 का उपयोग करके ईटी कोड को पूरी तरह से लिखना नहीं था। इसके अलावा मैंने नीचे दिखाई देने वाले कोड पॉलिशिंग के सभी पहलुओं के बारे में चिंता नहीं की थी।)

शुरू करने के लिए, मैंने lambdas का उपयोग नहीं किया - मैं स्पष्ट प्रकारों और कार्यों का उपयोग करना पसंद करता हूं - इसलिए मैं आपके प्रश्न के संबंध में लैम्ब्स के लिए/बहस नहीं करूंगा। मेरा अनुमान है कि वे मज़दूरों का उपयोग करने के समान होंगे और नीचे दिए गए गैर-ईटी कोड से बेहतर प्रदर्शन नहीं करेंगे (यानी, चाल की आवश्यकता होगी) - कम से कम जब तक कि कंपाइलर्स स्वचालित रूप से अपने स्वयं के आंतरिक ईटी का उपयोग करके लैम्बडा को अनुकूलित नहीं कर सकते। मैंने जो कोड लिखा है, वह सैमांतिक्स और सही अग्रेषण को स्थानांतरित करता है। यहां मैंने परिणामों के साथ शुरू किया और अंत में कोड प्रस्तुत किया।

मैंने math_vector<N> कक्षा बनाई जहां N==3 और यह std::array<long double, N> के आंतरिक निजी उदाहरण को परिभाषित करता है। सदस्य एक डिफ़ॉल्ट कन्स्ट्रक्टर हैं, वेक्टर और ऑपरेटर + = के तत्वों तक पहुंचने के लिए कन्स्ट्रक्टर और असाइनमेंट, एक प्रारंभकर्ता सूची कन्स्ट्रक्टर, एक विनाशक, एक स्वैप() सदस्य, ऑपरेटर [] को प्रतिलिपि बनाते हैं। किसी भी अभिव्यक्ति टेम्पलेट्स के बिना किया जाता है, इस कोड:

{ 
    cout << "CASE 1:\n"; 
    math_vector<3> a{1.0, 1.1, 1.2}; 
    math_vector<3> b{2.0, 2.1, 2.2}; 
    math_vector<3> c{3.0, 3.1, 3.2}; 
    math_vector<3> d{4.0, 4.1, 4.2}; 
    math_vector<3> result = a + b + c + d; 
    cout << '[' << &result << "]: " << result << "\n"; 
} 

आउटपुट (जब clang++ 3.1 या g++ 4.8 के साथ साथ संकलित - std=c++11 -O3):

CASE 1: 
0x7fff8d6edf50: math_vector(initlist) 
0x7fff8d6edef0: math_vector(initlist) 
0x7fff8d6ede90: math_vector(initlist) 
0x7fff8d6ede30: math_vector(initlist) 
0x7fff8d6edd70: math_vector(copy: 0x7fff8d6edf50) 
0x7fff8d6edda0: math_vector(move: 0x7fff8d6edd70) 
0x7fff8d6eddd0: math_vector(move: 0x7fff8d6edda0) 
0x7fff8d6edda0: ~math_vector() 
0x7fff8d6edd70: ~math_vector() 
[0x7fff8d6eddd0]: (10,10.4,10.8) 
0x7fff8d6eddd0: ~math_vector() 
0x7fff8d6ede30: ~math_vector() 
0x7fff8d6ede90: ~math_vector() 
0x7fff8d6edef0: ~math_vector() 
0x7fff8d6edf50: ~math_vector() 

यानी, चार स्पष्ट निर्माण प्रारंभकर्ता सूचियों का उपयोग उदाहरणों (यानी , initlist आइटम), result चर (यानी, 0x7fff8d6eddd0), और, अतिरिक्त तीन ऑब्जेक्ट्स कॉपी और चलती है।

{ 
    cout << "CASE 2:\n"; 
    math_vector<3> result = 
    math_vector<3>{1.0, 1.1, 1.2} + 
    math_vector<3>{2.0, 2.1, 2.2} + 
    math_vector<3>{3.0, 3.1, 3.2} + 
    math_vector<3>{4.0, 4.1, 4.2} 
    ; 
    cout << '[' << &result << "]: " << result << "\n"; 
} 

जो इस (फिर जब टिकट उपयोग नहीं किया जाता) आउटपुट:

करने के लिए केवल temporaries पर ध्यान केंद्रित करने और चलती है, मैं एक दूसरे मामले कि केवल बनाता result के रूप में एक नामित चर --all अन्य हैं rvalues ​​बनाया :

CASE 2: 
0x7fff8d6edcb0: math_vector(initlist) 
0x7fff8d6edc50: math_vector(initlist) 
0x7fff8d6edce0: math_vector(move: 0x7fff8d6edcb0) 
0x7fff8d6edbf0: math_vector(initlist) 
0x7fff8d6edd10: math_vector(move: 0x7fff8d6edce0) 
0x7fff8d6edb90: math_vector(initlist) 
0x7fff8d6edd40: math_vector(move: 0x7fff8d6edd10) 
0x7fff8d6edb90: ~math_vector() 
0x7fff8d6edd10: ~math_vector() 
0x7fff8d6edbf0: ~math_vector() 
0x7fff8d6edce0: ~math_vector() 
0x7fff8d6edc50: ~math_vector() 
0x7fff8d6edcb0: ~math_vector() 
[0x7fff8d6edd40]: (10,10.4,10.8) 
0x7fff8d6edd40: ~math_vector() 

जो बेहतर है: केवल अतिरिक्त कदम ऑब्जेक्ट का निर्माण होगा। सभी स्पष्ट रूप instantiated प्रकार अभी भी बनाया जा सकता है (यानी, चार initlist निर्माणकर्ता और result: के रूप में अगर मैं इसे एक सामान्य कोडिंग चेतावनी के साथ हार्ड-कोडेड मैं शून्य अतिरिक्त temporaries चाहता था और कोड के लिए:

लेकिन मैं बेहतर करना चाहता था)। इसे पूरा करने के मैं तो अभिव्यक्ति टेम्पलेट कोड इस प्रकार कहा:

  1. एक प्रॉक्सी math_vector_expr<LeftExpr,BinaryOp,RightExpr> वर्ग एक अभिव्यक्ति अभी तक गणना नहीं पकड़ करने के लिए बनाया गया था,
  2. एक प्रॉक्सी plus_op वर्ग इसके अलावा आपरेशन धारण करने के लिए बनाया गया था,
  3. एक math_vector_expr ऑब्जेक्ट को स्वीकार करने के लिए math_vector में एक कन्स्ट्रक्टर जोड़ा गया था, और
  4. अभिव्यक्ति टेम्पलेट के निर्माण को ट्रिगर करने के लिए "स्टार्टर" सदस्य फ़ंक्शन जोड़े गए थे।

ईटी का उपयोग करने वाले परिणाम अद्भुत हैं: किसी भी मामले में कोई अतिरिक्त अस्थायी नहीं! अब उत्पादन ऊपर पिछले दो मामलों:

CASE 1: 
0x7fffe7180c60: math_vector(initlist) 
0x7fffe7180c90: math_vector(initlist) 
0x7fffe7180cc0: math_vector(initlist) 
0x7fffe7180cf0: math_vector(initlist) 
0x7fffe7180d20: math_vector(expr: 0x7fffe7180d90) 
[0x7fffe7180d20]: (10,10.4,10.8) 
0x7fffe7180d20: ~math_vector() 
0x7fffe7180cf0: ~math_vector() 
0x7fffe7180cc0: ~math_vector() 
0x7fffe7180c90: ~math_vector() 
0x7fffe7180c60: ~math_vector() 

CASE 2: 
0x7fffe7180dd0: math_vector(initlist) 
0x7fffe7180e20: math_vector(initlist) 
0x7fffe7180e70: math_vector(initlist) 
0x7fffe7180eb0: math_vector(initlist) 
0x7fffe7180d20: math_vector(expr: 0x7fffe7180dc0) 
0x7fffe7180eb0: ~math_vector() 
0x7fffe7180e70: ~math_vector() 
0x7fffe7180e20: ~math_vector() 
0x7fffe7180dd0: ~math_vector() 
[0x7fffe7180d20]: (10,10.4,10.8) 
0x7fffe7180d20: ~math_vector() 

जैसे कि, ठीक 5 निर्माता कॉल और प्रत्येक मामले में 5 नाशक कॉल। समान (यहां तक ​​कि छोटे) g++ और clang++ आउटपुट के साथ

fldt 128(%rsp) 
leaq 128(%rsp), %rdi 
leaq 80(%rsp), %rbp 
fldt 176(%rsp) 
faddp %st, %st(1) 
fldt 224(%rsp) 
faddp %st, %st(1) 
fldt 272(%rsp) 
faddp %st, %st(1) 
fstpt 80(%rsp) 
fldt 144(%rsp) 
fldt 192(%rsp) 
faddp %st, %st(1) 
fldt 240(%rsp) 
faddp %st, %st(1) 
fldt 288(%rsp) 
faddp %st, %st(1) 
fstpt 96(%rsp) 
fldt 160(%rsp) 
fldt 208(%rsp) 
faddp %st, %st(1) 
fldt 256(%rsp) 
faddp %st, %st(1) 
fldt 304(%rsp) 
faddp %st, %st(1) 
fstpt 112(%rsp) 

: वास्तव में, यदि आप 4 initlist निर्माता कॉल और result एक के outputting के बीच कोडांतरक कोड उत्पन्न करने के लिए संकलक पूछना कोडांतरक कोड के इस सुंदर स्ट्रिंग हो जाता है कोड। कोई फ़ंक्शन कॉल नहीं, इत्यादि - जोड़ों का एक गुच्छा समायोजित करें जो वास्तव में एक चाहता है!

सी ++ 11 कोड इस प्रकार प्राप्त करने के लिए निम्नानुसार है। ईटीएस का उपयोग न करने के लिए बस #define DONT_USE_EXPR_TEMPL या ईटीएस का उपयोग करने के लिए इसे परिभाषित न करें।

#include <array> 
#include <algorithm> 
#include <initializer_list> 
#include <type_traits> 
#include <iostream> 

//#define DONT_USE_EXPR_TEMPL 

//=========================================================================== 

template <std::size_t N> class math_vector; 

template < 
    typename LeftExpr, 
    typename BinaryOp, 
    typename RightExpr 
> 
class math_vector_expr 
{ 
    public: 
    math_vector_expr() = delete; 

    math_vector_expr(LeftExpr l, RightExpr r) : 
     l_(std::forward<LeftExpr>(l)), 
     r_(std::forward<RightExpr>(r)) 
    { 
    } 

    // Prohibit copying... 
    math_vector_expr(math_vector_expr const&) = delete; 
    math_vector_expr& operator =(math_vector_expr const&) = delete; 

    // Allow moves... 
    math_vector_expr(math_vector_expr&&) = default; 
    math_vector_expr& operator =(math_vector_expr&&) = default; 

    template <typename RE> 
    auto operator +(RE&& re) const -> 
     math_vector_expr< 
     math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, 
     BinaryOp, 
     decltype(std::forward<RE>(re)) 
     > 
    { 
     return 
     math_vector_expr< 
      math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, 
      BinaryOp, 
      decltype(std::forward<RE>(re)) 
     >(*this, std::forward<RE>(re)) 
     ; 
    } 

    auto le() -> 
     typename std::add_lvalue_reference<LeftExpr>::type 
     { return l_; } 

    auto le() const -> 
     typename std::add_lvalue_reference< 
     typename std::add_const<LeftExpr>::type 
     >::type 
     { return l_; } 

    auto re() -> 
     typename std::add_lvalue_reference<RightExpr>::type 
     { return r_; } 

    auto re() const -> 
     typename std::add_lvalue_reference< 
     typename std::add_const<RightExpr>::type 
     >::type 
     { return r_; } 

    auto operator [](std::size_t index) const -> 
     decltype(
     BinaryOp::apply(this->le()[index], this->re()[index]) 
    ) 
    { 
     return BinaryOp::apply(le()[index], re()[index]); 
    } 

    private: 
    LeftExpr l_; 
    RightExpr r_; 
}; 

//=========================================================================== 

template <typename T> 
struct plus_op 
{ 
    static T apply(T const& a, T const& b) 
    { 
    return a + b; 
    } 

    static T apply(T&& a, T const& b) 
    { 
    a += b; 
    return std::move(a); 
    } 

    static T apply(T const& a, T&& b) 
    { 
    b += a; 
    return std::move(b); 
    } 

    static T apply(T&& a, T&& b) 
    { 
    a += b; 
    return std::move(a); 
    } 
}; 

//=========================================================================== 

template <std::size_t N> 
class math_vector 
{ 
    using impl_type = std::array<long double, N>; 

    public: 
    math_vector() 
    { 
     using namespace std; 
     fill(begin(v_), end(v_), impl_type{}); 
     std::cout << this << ": math_vector()" << endl; 
    } 

    math_vector(math_vector const& mv) noexcept 
    { 
     using namespace std; 
     copy(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector(copy: " << &mv << ")" << endl; 
    } 

    math_vector(math_vector&& mv) noexcept 
    { 
     using namespace std; 
     move(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector(move: " << &mv << ")" << endl; 
    } 

    math_vector(std::initializer_list<typename impl_type::value_type> l) 
    { 
     using namespace std; 
     copy(begin(l), end(l), begin(v_)); 
     std::cout << this << ": math_vector(initlist)" << endl; 
    } 

    math_vector& operator =(math_vector const& mv) noexcept 
    { 
     using namespace std; 
     copy(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector op =(copy: " << &mv << ")" << endl; 
     return *this; 
    } 

    math_vector& operator =(math_vector&& mv) noexcept 
    { 
     using namespace std; 
     move(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector op =(move: " << &mv << ")" << endl; 
     return *this; 
    } 

    ~math_vector() 
    { 
     using namespace std; 
     std::cout << this << ": ~math_vector()" << endl; 
    } 

    void swap(math_vector& mv) 
    { 
     using namespace std; 
     for (std::size_t i = 0; i<N; ++i) 
     swap(v_[i], mv[i]); 
    } 

    auto operator [](std::size_t index) const 
     -> typename impl_type::value_type const& 
    { 
     return v_[index]; 
    } 

    auto operator [](std::size_t index) 
     -> typename impl_type::value_type& 
    { 
     return v_[index]; 
    } 

    math_vector& operator +=(math_vector const& b) 
    { 
     for (std::size_t i = 0; i<N; ++i) 
     v_[i] += b[i]; 
     return *this; 
    } 

    #ifndef DONT_USE_EXPR_TEMPL 

    template <typename LE, typename Op, typename RE> 
    math_vector(math_vector_expr<LE,Op,RE>&& mve) 
    { 
     for (std::size_t i = 0; i < N; ++i) 
     v_[i] = mve[i]; 
     std::cout << this << ": math_vector(expr: " << &mve << ")" << std::endl; 
    } 

    template <typename RightExpr> 
    math_vector& operator =(RightExpr&& re) 
    { 
     for (std::size_t i = 0; i<N; ++i) 
     v_[i] = re[i]; 
     return *this; 
    } 

    template <typename RightExpr> 
    math_vector& operator +=(RightExpr&& re) 
    { 
     for (std::size_t i = 0; i<N; ++i) 
     v_[i] += re[i]; 
     return *this; 
    } 

    template <typename RightExpr> 
    auto operator +(RightExpr&& re) const -> 
     math_vector_expr< 
     math_vector const&, 
     plus_op<typename impl_type::value_type>, 
     decltype(std::forward<RightExpr>(re)) 
     > 
    { 
     return 
     math_vector_expr< 
      math_vector const&, 
      plus_op<typename impl_type::value_type>, 
      decltype(std::forward<RightExpr>(re)) 
     >(
      *this, 
      std::forward<RightExpr>(re) 
     ) 
     ; 
    } 

    #endif // #ifndef DONT_USE_EXPR_TEMPL 

    private: 
    impl_type v_; 
}; 

//=========================================================================== 

template <std::size_t N> 
inline void swap(math_vector<N>& a, math_vector<N>& b) 
{ 
    a.swap(b); 
} 

//=========================================================================== 

#ifdef DONT_USE_EXPR_TEMPL 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N> const& a, 
    math_vector<N> const& b 
) 
{ 
    math_vector<N> retval(a); 
    retval += b; 
    return retval; 
} 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N>&& a, 
    math_vector<N> const& b 
) 
{ 
    a += b; 
    return std::move(a); 
} 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N> const& a, 
    math_vector<N>&& b 
) 
{ 
    b += a; 
    return std::move(b); 
} 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N>&& a, 
    math_vector<N>&& b 
) 
{ 
    a += std::move(b); 
    return std::move(a); 
} 

#endif // #ifdef DONT_USE_EXPR_TEMPL 

//=========================================================================== 

template <std::size_t N> 
std::ostream& operator <<(std::ostream& os, math_vector<N> const& mv) 
{ 
    os << '('; 
    for (std::size_t i = 0; i < N; ++i) 
    os << mv[i] << ((i+1 != N) ? ',' : ')'); 
    return os; 
} 

//=========================================================================== 

int main() 
{ 
    using namespace std; 

    try 
    { 
    { 
     cout << "CASE 1:\n"; 
     math_vector<3> a{1.0, 1.1, 1.2}; 
     math_vector<3> b{2.0, 2.1, 2.2}; 
     math_vector<3> c{3.0, 3.1, 3.2}; 
     math_vector<3> d{4.0, 4.1, 4.2}; 
     math_vector<3> result = a + b + c + d; 
     cout << '[' << &result << "]: " << result << "\n"; 
    } 
    cout << endl; 
    { 
     cout << "CASE 2:\n"; 
     math_vector<3> result = 
     math_vector<3>{1.0, 1.1, 1.2} + 
     math_vector<3>{2.0, 2.1, 2.2} + 
     math_vector<3>{3.0, 3.1, 3.2} + 
     math_vector<3>{4.0, 4.1, 4.2} 
     ; 
     cout << '[' << &result << "]: " << result << "\n"; 
    } 
    } 
    catch (...) 
    { 
    return 1; 
    } 
} 

//=========================================================================== 
+2

धन्यवाद आपके उत्तर के लिए पॉल। आपके कोड में आपने अभिव्यक्ति टेम्पलेट तकनीक को अच्छी तरह से लागू किया है जो नई सी ++ 11 तकनीकों के साथ नमकीन है। अच्छा कार्य। मुझे अधिभारित ऑपरेशन कक्षाएं पसंद हैं। और हां, मैं मानता हूं कि लैम्बडा मूल रूप से उसी प्रदर्शन के लिए प्रस्तुत करेंगे जैसे कि structs नाम दिया गया है। मैंने ईटी तकनीक को बाधित करने की कोशिश की, अगर आपको लगता है कि प्रश्न मेरे कोड द्वारा बढ़ाया गया है। – ritter

+1

आपका स्वागत है! आपका प्रश्न भी बहुत प्रासंगिक है।नए जोड़ (विशेष रूप से विविध टेम्पलेट्स, सैमेंटिक्स ले जाते हैं, और सही अग्रेषण) वास्तव में हम सभी को फिर से समायोजित करने के लिए कदम उठाते हैं और चीजों को एक आसान तरीके से करते हैं - जैसे कि जब हमने कोडिंग शुरू की! शायद बहुत लंबे समय तक हम सभी संकलक को प्रभावी ढंग से बाईपास करने के लिए अनुकूलित कर रहे हैं। सी ++ 11 के साथ यह अब आवश्यक नहीं है क्योंकि विचार-विमर्श/सरल कोड संकलक को ऐसा करने के द्वारा बेहतर/इष्टतम प्रदर्शन उत्पन्न करता है। तो सी ++ 11 के साथ, मैं ऑप्टिमाइज़ करने के लिए प्रलोभन का विरोध करने के लिए खुद को याद दिलाने की कोशिश करता हूं लेकिन अधिक आसानी से पुनर्विचार करना चाहता हूं। –

+0

@ फ्रैंक: मुझे लगता है कि यह उत्तर स्वीकार करने के लिए तैयार है, है ना? – marton78

6

पॉल प्रेनी के कोड का सही संस्करण यहां दिया गया है। मैंने लेखक को ईमेल और टिप्पणियों में सूचित किया है; मैंने एक संपादन लिखा है, लेकिन इसे अयोग्य समीक्षकों द्वारा खारिज कर दिया गया है।

मूल कोड में त्रुटि यह है कि math_vector_expr :: ऑपरेटर + का BinaryOp टेम्पलेट पैरामीटर तय किया गया है।

#include <array> 
#include <algorithm> 
#include <initializer_list> 
#include <type_traits> 
#include <iostream> 

//#define DONT_USE_EXPR_TEMPL 

//=========================================================================== 

template <std::size_t N> class math_vector; 
template <typename T> struct plus_op; 


template < 
    typename LeftExpr, 
    typename BinaryOp, 
    typename RightExpr 
> 
class math_vector_expr 
{ 
    public: 
    typedef typename std::remove_reference<LeftExpr>::type::value_type value_type; 

    math_vector_expr() = delete; 

    math_vector_expr(LeftExpr l, RightExpr r) : 
     l_(std::forward<LeftExpr>(l)), 
     r_(std::forward<RightExpr>(r)) 
    { 
    } 

    // Prohibit copying... 
    math_vector_expr(math_vector_expr const&) = delete; 
    math_vector_expr& operator =(math_vector_expr const&) = delete; 

    // Allow moves... 
    math_vector_expr(math_vector_expr&&) = default; 
    math_vector_expr& operator =(math_vector_expr&&) = default; 

    template <typename RE> 
    auto operator +(RE&& re) const -> 
     math_vector_expr< 
     math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, 
     plus_op<value_type>, 
     decltype(std::forward<RE>(re)) 
     > 
    { 
     return 
     math_vector_expr< 
      math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, 
      plus_op<value_type>, 
      decltype(std::forward<RE>(re)) 
     >(*this, std::forward<RE>(re)) 
     ; 
    } 

    auto le() -> 
     typename std::add_lvalue_reference<LeftExpr>::type 
     { return l_; } 

    auto le() const -> 
     typename std::add_lvalue_reference< 
     typename std::add_const<LeftExpr>::type 
     >::type 
     { return l_; } 

    auto re() -> 
     typename std::add_lvalue_reference<RightExpr>::type 
     { return r_; } 

    auto re() const -> 
     typename std::add_lvalue_reference< 
     typename std::add_const<RightExpr>::type 
     >::type 
     { return r_; } 

    auto operator [](std::size_t index) const -> 
     value_type 
    { 
     return BinaryOp::apply(le()[index], re()[index]); 
    } 

    private: 
    LeftExpr l_; 
    RightExpr r_; 
}; 

//=========================================================================== 

template <typename T> 
struct plus_op 
{ 
    static T apply(T const& a, T const& b) 
    { 
    return a + b; 
    } 

    static T apply(T&& a, T const& b) 
    { 
    a += b; 
    return std::move(a); 
    } 

    static T apply(T const& a, T&& b) 
    { 
    b += a; 
    return std::move(b); 
    } 

    static T apply(T&& a, T&& b) 
    { 
    a += b; 
    return std::move(a); 
    } 
}; 

//=========================================================================== 

template <std::size_t N> 
class math_vector 
{ 
    using impl_type = std::array<long double, N>; 

    public: 
    typedef typename impl_type::value_type value_type; 

    math_vector() 
    { 
     using namespace std; 
     fill(begin(v_), end(v_), impl_type{}); 
     std::cout << this << ": math_vector()" << endl; 
    } 

    math_vector(math_vector const& mv) noexcept 
    { 
     using namespace std; 
     copy(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector(copy: " << &mv << ")" << endl; 
    } 

    math_vector(math_vector&& mv) noexcept 
    { 
     using namespace std; 
     move(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector(move: " << &mv << ")" << endl; 
    } 

    math_vector(std::initializer_list<value_type> l) 
    { 
     using namespace std; 
     copy(begin(l), end(l), begin(v_)); 
     std::cout << this << ": math_vector(initlist)" << endl; 
    } 

    math_vector& operator =(math_vector const& mv) noexcept 
    { 
     using namespace std; 
     copy(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector op =(copy: " << &mv << ")" << endl; 
     return *this; 
    } 

    math_vector& operator =(math_vector&& mv) noexcept 
    { 
     using namespace std; 
     move(begin(mv.v_), end(mv.v_), begin(v_)); 
     std::cout << this << ": math_vector op =(move: " << &mv << ")" << endl; 
     return *this; 
    } 

    ~math_vector() 
    { 
     using namespace std; 
     std::cout << this << ": ~math_vector()" << endl; 
    } 

    void swap(math_vector& mv) 
    { 
     using namespace std; 
     for (std::size_t i = 0; i<N; ++i) 
     swap(v_[i], mv[i]); 
    } 

    auto operator [](std::size_t index) const 
     -> value_type const& 
    { 
     return v_[index]; 
    } 

    auto operator [](std::size_t index) 
     -> value_type& 
    { 
     return v_[index]; 
    } 

    math_vector& operator +=(math_vector const& b) 
    { 
     for (std::size_t i = 0; i<N; ++i) 
     v_[i] += b[i]; 
     return *this; 
    } 

    #ifndef DONT_USE_EXPR_TEMPL 

    template <typename LE, typename Op, typename RE> 
    math_vector(math_vector_expr<LE,Op,RE>&& mve) 
    { 
     for (std::size_t i = 0; i < N; ++i) 
     v_[i] = mve[i]; 
     std::cout << this << ": math_vector(expr: " << &mve << ")" << std::endl; 
    } 

    template <typename RightExpr> 
    math_vector& operator =(RightExpr&& re) 
    { 
     for (std::size_t i = 0; i<N; ++i) 
     v_[i] = re[i]; 
     return *this; 
    } 

    template <typename RightExpr> 
    math_vector& operator +=(RightExpr&& re) 
    { 
     for (std::size_t i = 0; i<N; ++i) 
     v_[i] += re[i]; 
     return *this; 
    } 

    template <typename RightExpr> 
    auto operator +(RightExpr&& re) const -> 
     math_vector_expr< 
     math_vector const&, 
     plus_op<value_type>, 
     decltype(std::forward<RightExpr>(re)) 
     > 
    { 
     return 
     math_vector_expr< 
      math_vector const&, 
      plus_op<value_type>, 
      decltype(std::forward<RightExpr>(re)) 
     >(
      *this, 
      std::forward<RightExpr>(re) 
     ) 
     ; 
    } 

    #endif // #ifndef DONT_USE_EXPR_TEMPL 

    private: 
    impl_type v_; 
}; 

//=========================================================================== 

template <std::size_t N> 
inline void swap(math_vector<N>& a, math_vector<N>& b) 
{ 
    a.swap(b); 
} 

//=========================================================================== 

#ifdef DONT_USE_EXPR_TEMPL 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N> const& a, 
    math_vector<N> const& b 
) 
{ 
    math_vector<N> retval(a); 
    retval += b; 
    return retval; 
} 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N>&& a, 
    math_vector<N> const& b 
) 
{ 
    a += b; 
    return std::move(a); 
} 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N> const& a, 
    math_vector<N>&& b 
) 
{ 
    b += a; 
    return std::move(b); 
} 

template <std::size_t N> 
inline math_vector<N> operator +(
    math_vector<N>&& a, 
    math_vector<N>&& b 
) 
{ 
    a += std::move(b); 
    return std::move(a); 
} 

#endif // #ifdef DONT_USE_EXPR_TEMPL 

//=========================================================================== 

template <std::size_t N> 
std::ostream& operator <<(std::ostream& os, math_vector<N> const& mv) 
{ 
    os << '('; 
    for (std::size_t i = 0; i < N; ++i) 
    os << mv[i] << ((i+1 != N) ? ',' : ')'); 
    return os; 
} 

//=========================================================================== 

int main() 
{ 
    using namespace std; 

    try 
    { 
    { 
     cout << "CASE 1:\n"; 
     math_vector<3> a{1.0, 1.1, 1.2}; 
     math_vector<3> b{2.0, 2.1, 2.2}; 
     math_vector<3> c{3.0, 3.1, 3.2}; 
     math_vector<3> d{4.0, 4.1, 4.2}; 
     math_vector<3> result = a + b + c + d; 
     cout << '[' << &result << "]: " << result << "\n"; 
    } 
    cout << endl; 
    { 
     cout << "CASE 2:\n"; 
     math_vector<3> result = 
     math_vector<3>{1.0, 1.1, 1.2} + 
     math_vector<3>{2.0, 2.1, 2.2} + 
     math_vector<3>{3.0, 3.1, 3.2} + 
     math_vector<3>{4.0, 4.1, 4.2} 
     ; 
     cout << '[' << &result << "]: " << result << "\n"; 
    } 
    } 
    catch (...) 
    { 
    return 1; 
    } 
} 

//===========================================================================