खोज…


परिचय

सी ++ में, उपयोगकर्ता-परिभाषित प्रकारों के लिए + और -> जैसे ऑपरेटरों को परिभाषित करना संभव है। उदाहरण के लिए, <string> हैडर एक को परिभाषित करता है + CONCATENATE तार करने के लिए ऑपरेटर। यह operator कीवर्ड का उपयोग करके एक ऑपरेटर फ़ंक्शन को परिभाषित करके किया जाता है।

टिप्पणियों

बिल्ट-इन प्रकारों के ऑपरेटरों को बदला नहीं जा सकता है, ऑपरेटर केवल उपयोगकर्ता-परिभाषित प्रकारों के लिए अतिभारित हो सकते हैं। यही है, कम से कम ऑपरेंड्स में से एक उपयोगकर्ता-परिभाषित प्रकार का होना चाहिए।

निम्नलिखित ऑपरेटरों को अतिभारित नहीं किया जा सकता है:

  • सदस्य पहुंच या "डॉट" ऑपरेटर .
  • सदस्य अभिगम ऑपरेटर के लिए सूचक .*
  • गुंजाइश रिज़ॉल्यूशन ऑपरेटर, ::
  • टर्नरी सशर्त ऑपरेटर ; ?:
  • dynamic_cast , static_cast , reinterpret_cast , const_cast , typeid , sizeof , alignof , और noexcept
  • प्रीप्रोसेसिंग निर्देश, # और ## , जो किसी भी प्रकार की जानकारी उपलब्ध होने से पहले निष्पादित किए जाते हैं।

कुछ ऑपरेटर हैं जिन्हें आपको (99.98% समय) ओवरलोड नहीं करना चाहिए:

  • && और || (पसंद करें, इसके बजाय, bool निहित रूपांतरण का उपयोग करें)
  • ,
  • पता-के ऑपरेटर (एकल & )

क्यों? क्योंकि वे ऑपरेटरों को ओवरलोड करते हैं जो दूसरे प्रोग्रामर से कभी उम्मीद नहीं कर सकते हैं, जिसके परिणामस्वरूप प्रत्याशित की तुलना में अलग व्यवहार होता है।

उदाहरण के लिए, उपयोगकर्ता परिभाषित && और || इन ऑपरेटरों के अधिभार अपने शॉर्ट-सर्किट मूल्यांकन को खो देते हैं और अपने विशेष अनुक्रमण गुणों (सी ++ 17) को खो देते हैं , अनुक्रमण मुद्दा भी लागू होता है , ऑपरेटर ओवरलोड करता है।

अंकगणितीय आपरेटर

आप सभी बुनियादी अंकगणितीय ऑपरेटरों को अधिभारित कर सकते हैं:

  • + और +=
  • - और -=
  • * और *=
  • / और /=
  • & &=
  • | और |=
  • ^ और ^=
  • >> और >>=
  • << और <<=

सभी ऑपरेटरों के लिए ओवरलोडिंग समान है। स्पष्टीकरण के लिए नीचे स्क्रॉल करें

class / struct बाहर ओवरलोडिंग:

//operator+ should be implemented in terms of operator+=
T operator+(T lhs, const T& rhs)
{
    lhs += rhs;
    return lhs;
}

T& operator+=(T& lhs, const T& rhs)
{
    //Perform addition
    return lhs;
}

class / struct अंदर ओवरलोडिंग:

//operator+ should be implemented in terms of operator+=
T operator+(const T& rhs)
{
    *this += rhs;
    return *this;
}

T& operator+=(const T& rhs)
{
    //Perform addition
    return *this;
}

नोट: operator+ को नॉन-कास्ट मान द्वारा वापस लौटना चाहिए, क्योंकि एक संदर्भ देने से कोई मतलब नहीं होगा (यह एक नई वस्तु लौटाता है) और न ही एक const वैल्यू लौटाएगा (आपको आम तौर पर const द्वारा वापस नहीं लौटना चाहिए)। पहला तर्क मूल्य द्वारा पारित किया जाता है, क्यों? इसलिये

  1. आप मूल वस्तु को संशोधित नहीं कर सकते हैं ( Object foobar = foo + bar; foo को संशोधित नहीं करना चाहिए, इससे कोई मतलब नहीं होगा)
  2. आप इसे const नहीं कर सकते, क्योंकि आपको ऑब्जेक्ट को संशोधित करने में सक्षम होना होगा (क्योंकि operator+ संदर्भ में लागू होता है operator+= , जो ऑब्जेक्ट को संशोधित करता है)

const& पासिंग एक विकल्प होगा, लेकिन फिर आपको पास की गई वस्तु की एक अस्थायी प्रतिलिपि बनानी होगी। मूल्य से गुजरकर, कंपाइलर आपके लिए करता है।


operator+= स्वयं के लिए एक संदर्भ देता है, क्योंकि तब उन्हें श्रृंखलाबद्ध करना संभव है (उसी चर का उपयोग न करें, हालांकि अनुक्रम बिंदुओं के कारण यह अपरिभाषित व्यवहार होगा)।

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

गैर-संचालक

आप 2 यूनिरी ऑपरेटर्स को ओवरलोड कर सकते हैं:

  • ++foo और foo++
  • --foo और foo--

ओवरलोडिंग दोनों प्रकारों ( ++ और -- ) के लिए समान है। स्पष्टीकरण के लिए नीचे स्क्रॉल करें

class / struct बाहर ओवरलोडिंग:

//Prefix operator ++foo
T& operator++(T& lhs)
{
    //Perform addition
    return lhs;
}

//Postfix operator foo++ (int argument is used to separate pre- and postfix) 
//Should be implemented in terms of ++foo (prefix operator)
T operator++(T& lhs, int)
{
    T t(lhs);
    ++lhs;
    return t;
}

class / struct अंदर ओवरलोडिंग:

//Prefix operator ++foo
T& operator++()
{
    //Perform addition
    return *this;
}

//Postfix operator foo++ (int argument is used to separate pre- and postfix) 
//Should be implemented in terms of ++foo (prefix operator)
T operator++(int)
{
    T t(*this);
    ++(*this);
    return t;
}

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


पोस्टफ़िक्स ऑपरेटर अस्थायी (पिछला मान) मान से लौटता है, और इसलिए यह एक संदर्भ नहीं हो सकता है, क्योंकि यह एक अस्थायी के लिए एक संदर्भ होगा, जो फ़ंक्शन के अंत में कचरा मूल्य होगा, क्योंकि अस्थायी चर निकल जाता है की गुंजाइश)। यह भी const नहीं हो सकता, क्योंकि आपको इसे सीधे संशोधित करने में सक्षम होना चाहिए।

पहला तर्क "कॉलिंग" ऑब्जेक्ट के लिए एक नॉन- const संदर्भ है, क्योंकि यदि यह const , तो आप इसे संशोधित नहीं कर पाएंगे, और यदि यह एक संदर्भ नहीं थे, तो आप मूल मान को नहीं बदलेंगे।

यह उपसर्ग ऑपरेटर अधिभार में आवश्यक नकल की वजह से है कि इसे छोरों के for उपसर्ग ++ के बजाय उपसर्ग ++ का उपयोग करने की आदत बनाना बेहतर है। से for पाश परिप्रेक्ष्य, वे आमतौर पर कार्यात्मक रूप से बराबर कर रहे हैं, लेकिन वहाँ विशेष रूप से सदस्यों को कॉपी करने के का एक बहुत के साथ "वसा" वर्गों के साथ, उपसर्ग ++ का उपयोग करने के लिए एक मामूली प्रदर्शन लाभ हो सकता है। लूप के लिए उपसर्ग ++ का उपयोग करने का उदाहरण:

for (list<string>::const_iterator it = tokens.begin();
     it != tokens.end();
     ++it) { // Don't use it++
    ...
}

तुलनात्मक संचालक

आप सभी तुलना ऑपरेटरों को अधिभारित कर सकते हैं:

  • == और !=
  • > और <
  • >= और <=

उन सभी ऑपरेटरों को ओवरलोड करने का अनुशंसित तरीका केवल 2 ऑपरेटरों ( == और < ) को लागू करने और फिर बाकी को परिभाषित करने के लिए उपयोग करना है। स्पष्टीकरण के लिए नीचे स्क्रॉल करें

class / struct बाहर ओवरलोडिंग:

//Only implement those 2
bool operator==(const T& lhs, const T& rhs) { /* Compare */ }
bool operator<(const T& lhs, const T& rhs) { /* Compare */ }

//Now you can define the rest
bool operator!=(const T& lhs, const T& rhs) { return !(lhs == rhs); }
bool operator>(const T& lhs, const T& rhs) { return rhs < lhs; }
bool operator<=(const T& lhs, const T& rhs) { return !(lhs > rhs); }
bool operator>=(const T& lhs, const T& rhs) { return !(lhs < rhs); }

class / struct अंदर ओवरलोडिंग:

//Note that the functions are const, because if they are not const, you wouldn't be able
//to call them if the object is const

//Only implement those 2
bool operator==(const T& rhs) const { /* Compare */ }
bool operator<(const T& rhs) const { /* Compare */ }

//Now you can define the rest
bool operator!=(const T& rhs) const { return !(*this == rhs); }
bool operator>(const T& rhs) const { return rhs < *this; }
bool operator<=(const T& rhs) const { return !(*this > rhs); }
bool operator>=(const T& rhs) const { return !(*this < rhs); }

ऑपरेटर स्पष्ट रूप से एक bool वापस करते हैं, जो संबंधित ऑपरेशन के लिए true या false संकेत देता true

सभी संचालक अपने तर्क const& द्वारा लेते हैं, क्योंकि केवल एक ही चीज़ जो ऑपरेटर करते हैं, तुलना की जाती है, इसलिए उन्हें वस्तुओं को संशोधित नहीं करना चाहिए। द्वारा पासिंग & (संदर्भ) तेजी से मूल्य की तुलना में है, और यह सुनिश्चित करें कि ऑपरेटरों इसे संशोधित नहीं करना है, यह एक है const -reference।

ध्यान दें कि class / struct अंदर के संचालकों को const रूप में परिभाषित किया गया है, इसका कारण यह है कि फ़ंक्शंस const होने के बिना, const ऑब्जेक्ट्स की तुलना करना संभव नहीं होगा, क्योंकि कंपाइलर को पता नहीं है कि ऑपरेटर कुछ भी संशोधित नहीं करते हैं।

रूपांतरण ऑपरेटर

आप टाइप ऑपरेटरों को अधिभारित कर सकते हैं, ताकि आपके प्रकार को स्पष्ट रूप से निर्दिष्ट प्रकार में परिवर्तित किया जा सके।

रूपांतरण ऑपरेटर को एक class / struct में परिभाषित किया जाना चाहिए:

operator T() const { /* return something */ }

ध्यान दें: ऑपरेटर है const अनुमति देने के लिए const वस्तुओं परिवर्तित किया।

उदाहरण:

struct Text
{
    std::string text;

    // Now Text can be implicitly converted into a const char*
    /*explicit*/ operator const char*() const { return text.data(); }
    // ^^^^^^^
    // to disable implicit conversion
};

Text t;
t.text = "Hello world!";

//Ok
const char* copyoftext = t;

सबस्क्रिप्ट ऑपरेटर

आप सरणी सबस्क्रिप्ट ऑपरेटर [] को भी अधिभारित कर सकते हैं।

आपको हमेशा (99.98% समय) 2 संस्करणों, एक const और नॉट- const संस्करण को लागू करना चाहिए, क्योंकि यदि ऑब्जेक्ट const , तो यह [] द्वारा लौटाए गए ऑब्जेक्ट को संशोधित करने में सक्षम नहीं होना चाहिए।

तर्कों को मूल्य के बजाय const& द्वारा पारित किया जाता है क्योंकि संदर्भ से गुजरना मूल्य से अधिक तेज है, और const इसलिए कि ऑपरेटर गलती से सूचकांक को नहीं बदलता है।

ऑपरेटर संदर्भ द्वारा लौटते हैं, क्योंकि डिज़ाइन द्वारा आप वस्तु को संशोधित कर सकते हैं [] वापसी, अर्थात:

std::vector<int> v{ 1 };
v[0] = 2; //Changes value of 1 to 2
          //wouldn't be possible if not returned by reference

आप केवल एक class / struct अंदर ही ओवरलोड कर सकते हैं:

//I is the index type, normally an int
T& operator[](const I& index)
{
    //Do something
    //return something
}

//I is the index type, normally an int
const T& operator[](const I& index) const
{
    //Do something
    //return something
}

एकाधिक सबस्क्रिप्ट ऑपरेटर, [][]... , प्रॉक्सी ऑब्जेक्ट के माध्यम से प्राप्त किया जा सकता है। एक सरल पंक्ति-प्रमुख मैट्रिक्स वर्ग का निम्न उदाहरण यह प्रदर्शित करता है:

template<class T>
class matrix {
    // class enabling [][] overload to access matrix elements
    template <class C>
    class proxy_row_vector {
        using reference = decltype(std::declval<C>()[0]);
        using const_reference = decltype(std::declval<C const>()[0]);
    public:
        proxy_row_vector(C& _vec, std::size_t _r_ind, std::size_t _cols)
            : vec(_vec), row_index(_r_ind), cols(_cols) {}
        const_reference operator[](std::size_t _col_index) const {
            return vec[row_index*cols + _col_index];
        }
        reference operator[](std::size_t _col_index) {
            return vec[row_index*cols + _col_index];
        }
    private:
        C& vec;
        std::size_t row_index; // row index to access
        std::size_t cols; // number of columns in matrix
    };

    using const_proxy = proxy_row_vector<const std::vector<T>>;
    using proxy = proxy_row_vector<std::vector<T>>;
public:
    matrix() : mtx(), rows(0), cols(0) {}
    matrix(std::size_t _rows, std::size_t _cols)
        : mtx(_rows*_cols), rows(_rows), cols(_cols) {}

    // call operator[] followed by another [] call to access matrix elements
    const_proxy operator[](std::size_t _row_index) const {
        return const_proxy(mtx, _row_index, cols);
    }

    proxy operator[](std::size_t _row_index) {
        return proxy(mtx, _row_index, cols);
    }
private:
    std::vector<T> mtx;
    std::size_t rows;
    std::size_t cols;
};

फंक्शन कॉल ऑपरेटर

आप फ़ंक्शन कॉल ऑपरेटर को ओवरलोड कर सकते हैं () :

ओवरलोडिंग एक class / struct अंदर की जानी चाहिए:

//R -> Return type
//Types -> any different type
R operator()(Type name, Type2 name2, ...)
{
    //Do something
    //return something
}

//Use it like this (R is return type, a and b are variables)
R foo = object(a, b, ...);

उदाहरण के लिए:

struct Sum
{
    int operator()(int a, int b)
    {
        return a + b;
    }
};

//Create instance of struct
Sum sum;
int result = sum(1, 1); //result == 2

असाइनमेंट ऑपरेटर

असाइनमेंट ऑपरेटर सबसे महत्वपूर्ण ऑपरेटरों में से एक है क्योंकि यह आपको एक चर की स्थिति को बदलने की अनुमति देता है।

यदि आप अपने class / struct लिए एसिगमेंट ऑपरेटर को अधिभार नहीं देते हैं, तो यह संकलक द्वारा स्वचालित रूप से उत्पन्न होता है: स्वचालित रूप से उत्पन्न असाइनमेंट ऑपरेटर एक "सदस्यवार असाइनमेंट" करता है, अर्थात सभी सदस्यों पर असाइनमेंट ऑपरेटरों को आमंत्रित करके, इसलिए एक ऑब्जेक्ट कॉपी किया जाता है; दूसरे, समय पर एक सदस्य। असाइनमेंट ऑपरेटर को तब ओवरलोड किया जाना चाहिए जब साधारण सदस्यवार असाइनमेंट आपकी class / struct के लिए उपयुक्त न हो, उदाहरण के लिए यदि आपको किसी ऑब्जेक्ट की गहरी कॉपी करने की आवश्यकता है।

असाइनमेंट ऑपरेटर को ओवरलोड करना = आसान है, लेकिन आपको कुछ सरल चरणों का पालन करना चाहिए।

  1. स्व-असाइनमेंट के लिए टेस्ट। यह जाँच दो कारणों से महत्वपूर्ण है:
    • एक स्व-असाइनमेंट एक अनावश्यक प्रतिलिपि है, इसलिए इसे प्रदर्शन करने का कोई मतलब नहीं है;
    • अगला चरण स्व-असाइनमेंट के मामले में काम नहीं करेगा।
  2. पुराने डेटा को साफ करें। पुराने डेटा को नए से बदला जाना चाहिए। अब, आप पिछले चरण के दूसरे कारण को समझ सकते हैं: यदि ऑब्जेक्ट की सामग्री नष्ट हो गई थी, तो एक स्व-असाइनमेंट कॉपी प्रदर्शन करने में विफल हो जाएगा।
  3. सभी सदस्यों को कॉपी करें। यदि आप अपनी class या अपनी struct लिए एसिगमेंट ऑपरेटर को ओवरलोड करते हैं, तो यह संकलक द्वारा स्वचालित रूप से उत्पन्न नहीं होता है, इसलिए आपको सभी सदस्यों को अन्य ऑब्जेक्ट से कॉपी करने का प्रभार लेने की आवश्यकता होगी।
  4. वापसी *this । ऑपरेटर संदर्भ द्वारा अपने आप वापस आ जाता है, क्योंकि यह चैनिंग (यानी int b = (a = 6) + 4; //b == 10 ) की अनुमति देता है।
//T is some type
T& operator=(const T& other)
{
    //Do something (like copying values)
    return *this;
}

नोट: other द्वारा पारित कर दिया है const& , क्योंकि वस्तु निर्धारित किये जाने से बदला नहीं जाना चाहिए, और मूल्य की तुलना में संदर्भ द्वारा पारित करने के लिए तेजी से है, और की तुलना में सुनिश्चित करने के लिए operator= यह गलती को संशोधित नहीं है, यह है const

असाइनमेंट ऑपरेटर को केवल class / struct में ओवरलोड किया जा सकता है, क्योंकि = का लेफ्ट वैल्यू हमेशा class / struct ही होता है। एक नि: शुल्क फ़ंक्शन के रूप में इसे परिभाषित करने की यह गारंटी नहीं है, और इसकी वजह से यह अस्वीकृत है।

जब आप इसे class / struct में घोषित करते हैं, तो लेफ्ट वैल्यू का मतलब class / struct ही होता है, इसलिए इसमें कोई समस्या नहीं है।

संचालक नहीं है

बिटवाइज़ को ओवरलोड नहीं करना ( ~ ) काफी सरल है। स्पष्टीकरण के लिए नीचे स्क्रॉल करें

class / struct बाहर ओवरलोडिंग:

T operator~(T lhs)
{
    //Do operation
    return lhs;
}

class / struct अंदर ओवरलोडिंग:

T operator~()
{
    T t(*this);
    //Do operation
    return t;
}

नोट: operator~ मूल्य से वापस आ जाता है, क्योंकि उसे एक नया मान (संशोधित मूल्य) वापस करना पड़ता है, और मूल्य का संदर्भ नहीं होता है (यह अस्थायी वस्तु का संदर्भ होगा, जिसमें जैसे ही कचरा मूल्य होगा) ऑपरेटर किया जाता है)। या तो const न करें क्योंकि कॉलिंग कोड बाद में इसे संशोधित करने में सक्षम होना चाहिए (यानी int a = ~a + 1; संभव होना चाहिए)।

अंदर class / struct , आप एक अस्थायी वस्तु बनाने के लिए है, क्योंकि आप संशोधित नहीं कर सकते this , के रूप में यह मूल वस्तु है, जो मामला नहीं होना चाहिए संशोधित करेगा।

I / O के लिए बिट शिफ्ट ऑपरेटर

ऑपरेटरों << और >> आमतौर पर "लिखने" और "पढ़ने" ऑपरेटरों के रूप में उपयोग किया जाता है:

  • std::ostream overloads << अंतर्निहित स्ट्रीम में चर लिखने के लिए (उदाहरण: std::cout )
  • std::istream overloads >> अंतर्निहित स्ट्रीम से चर तक पढ़ने के लिए (उदाहरण: std::cin )

जिस तरह से वे ऐसा करते हैं यदि आप उन्हें class / struct बाहर "सामान्य रूप से" ओवरलोड करना चाहते हैं, सिवाय इसके कि तर्कों को निर्दिष्ट करना एक ही प्रकार का नहीं है:

  • रिटर्न प्रकार वह स्ट्रीम है जिसे आप ओवरलोड करना चाहते हैं (उदाहरण के लिए, std::ostream ) संदर्भ के आधार पर, चैनिंग की अनुमति देने के लिए (चैनिंग: std::cout << a << b; )। उदाहरण: std::ostream&
  • lhs रिटर्न प्रकार के समान होगा
  • rhs वह प्रकार है जिसे आप प्रदर्शन कारण के लिए मान के बजाय (यानी T ) से ओवरलोडिंग की अनुमति देना चाहते हैं, जिसे const& द्वारा पास किया गया है ( rhs को बदला नहीं जाना चाहिए)। उदाहरण: const Vector&

उदाहरण:

//Overload std::ostream operator<< to allow output from Vector's
std::ostream& operator<<(std::ostream& lhs, const Vector& rhs)
{
    lhs << "x: " << rhs.x << " y: " << rhs.y << " z: " << rhs.z << '\n';
    return lhs;
}

Vector v = { 1, 2, 3};

//Now you can do
std::cout << v;

जटिल संख्या पर दोबारा गौर किया गया

नीचे दिए गए कोड एक बहुत ही सरल जटिल संख्या प्रकार को लागू करते हैं, जिसके लिए भाषा का प्रकार पदोन्नति नियमों का पालन करते हुए अंतर्निहित फ़ील्ड को स्वचालित रूप से बढ़ावा दिया जाता है, एक अलग क्षेत्र के सदस्य के साथ चार बुनियादी ऑपरेटरों (+, -, *, और /) के आवेदन के तहत। (यह एक और complex<T> या कुछ स्केलर प्रकार हो)।

इसका उद्देश्य एक समग्र उदाहरण होना है, जो टेम्प्लेट के बुनियादी उपयोग के साथ-साथ ओवरलोडिंग ऑपरेटर को कवर करता है।

#include <type_traits>

namespace not_std{

using std::decay_t;

//----------------------------------------------------------------
// complex< value_t >
//----------------------------------------------------------------

template<typename value_t>
struct complex
{
    value_t x;
    value_t y;

    complex &operator += (const value_t &x)
    {
        this->x += x;
        return *this;
    }
    complex &operator += (const complex &other)
    {
        this->x += other.x;
        this->y += other.y;
        return *this;
    }

    complex &operator -= (const value_t &x)
    {
        this->x -= x;
        return *this;
    }
    complex &operator -= (const complex &other)
    {
        this->x -= other.x;
        this->y -= other.y;
        return *this;
    }

    complex &operator *= (const value_t &s)
    {
        this->x *= s;
        this->y *= s;
        return *this;
    }
    complex &operator *= (const complex &other)
    {
        (*this) = (*this) * other;
        return *this;
    }

    complex &operator /= (const value_t &s)
    {
        this->x /= s;
        this->y /= s;
        return *this;
    }
    complex &operator /= (const complex &other)
    {
        (*this) = (*this) / other;
        return *this;
    }

    complex(const value_t &x, const value_t &y)
    : x{x}
    , y{y}
    {}

    template<typename other_value_t>
    explicit complex(const complex<other_value_t> &other)
    : x{static_cast<const value_t &>(other.x)}
    , y{static_cast<const value_t &>(other.y)}
    {}

    complex &operator = (const complex &) = default;
    complex &operator = (complex &&) = default;
    complex(const complex &) = default;
    complex(complex &&) = default;
    complex() = default;
};

// Absolute value squared
template<typename value_t>
value_t absqr(const complex<value_t> &z)
{ return z.x*z.x + z.y*z.y; }

//----------------------------------------------------------------
// operator - (negation)
//----------------------------------------------------------------

template<typename value_t>
complex<value_t> operator - (const complex<value_t> &z)
{ return {-z.x, -z.y}; }

//----------------------------------------------------------------
// operator +
//----------------------------------------------------------------

template<typename left_t,typename right_t>
auto operator + (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x + b.x)>>
{ return{a.x + b.x, a.y + b.y}; }

template<typename left_t,typename right_t>
auto operator + (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a + b.x)>>
{ return{a + b.x, b.y}; }

template<typename left_t,typename right_t>
auto operator + (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x + b)>>
{ return{a.x + b, a.y}; }

//----------------------------------------------------------------
// operator -
//----------------------------------------------------------------

template<typename left_t,typename right_t>
auto operator - (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x - b.x)>>
{ return{a.x - b.x, a.y - b.y}; }

template<typename left_t,typename right_t>
auto operator - (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a - b.x)>>
{ return{a - b.x, - b.y}; }

template<typename left_t,typename right_t>
auto operator - (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x - b)>>
{ return{a.x - b, a.y}; }

//----------------------------------------------------------------
// operator *
//----------------------------------------------------------------

template<typename left_t, typename right_t>
auto operator * (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x * b.x)>>
{
    return {
        a.x*b.x - a.y*b.y,
        a.x*b.y + a.y*b.x
        };
}

template<typename left_t, typename right_t>
auto operator * (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a * b.x)>>
{ return {a * b.x, a * b.y}; }

template<typename left_t, typename right_t>
auto operator * (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x * b)>>
{ return {a.x * b, a.y * b}; }

//----------------------------------------------------------------
// operator /
//----------------------------------------------------------------

template<typename left_t, typename right_t>
auto operator / (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x / b.x)>>
{
    const auto r = absqr(b);
    return {
        ( a.x*b.x + a.y*b.y) / r,
        (-a.x*b.y + a.y*b.x) / r
        };
}

template<typename left_t, typename right_t>
auto operator / (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a / b.x)>>
{
    const auto s = a/absqr(b);
    return {
         b.x * s,
        -b.y * s
        };
}

template<typename left_t, typename right_t>
auto operator / (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x / b)>>
{ return {a.x / b, a.y / b}; }

}// namespace not_std


int main(int argc, char **argv)
{
    using namespace not_std;

    complex<float> fz{4.0f, 1.0f};

    // makes a complex<double>
    auto dz = fz * 1.0;

    // still a complex<double>
    auto idz = 1.0f/dz;

    // also a complex<double>
    auto one = dz * idz;

    // a complex<double> again
    auto one_again = fz * idz;

    // Operator tests, just to make sure everything compiles.

    complex<float> a{1.0f, -2.0f};
    complex<double> b{3.0, -4.0};

    // All of these are complex<double>
    auto c0 = a + b;
    auto c1 = a - b;
    auto c2 = a * b;
    auto c3 = a / b;

    // All of these are complex<float>
    auto d0 = a + 1;
    auto d1 = 1 + a;
    auto d2 = a - 1;
    auto d3 = 1 - a;
    auto d4 = a * 1;
    auto d5 = 1 * a;
    auto d6 = a / 1;
    auto d7 = 1 / a;

    // All of these are complex<double>
    auto e0 = b + 1;
    auto e1 = 1 + b;
    auto e2 = b - 1;
    auto e3 = 1 - b;
    auto e4 = b * 1;
    auto e5 = 1 * b;
    auto e6 = b / 1;
    auto e7 = 1 / b;

    return 0;
}

नामांकित संचालक

आप नामांकित ऑपरेटरों के साथ C ++ का विस्तार कर सकते हैं जो मानक C ++ ऑपरेटरों द्वारा "उद्धृत" किए गए हैं।

पहले हम एक दर्जन से अधिक पुस्तकालय शुरू करते हैं:

namespace named_operator {
  template<class D>struct make_operator{constexpr make_operator(){}};

  template<class T, char, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
    return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
  -> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
  {
    return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

यह अभी तक कुछ नहीं करता है।

सबसे पहले, संलग्न वैक्टर

namespace my_ns {
  struct append_t : named_operator::make_operator<append_t> {};
  constexpr append_t append{};
  
  template<class T, class A0, class A1>
  std::vector<T, A0> named_invoke( std::vector<T, A0> lhs, append_t, std::vector<T, A1> const& rhs ) {
      lhs.insert( lhs.end(), rhs.begin(), rhs.end() );
      return std::move(lhs);
  }
}
using my_ns::append;

std::vector<int> a {1,2,3};
std::vector<int> b {4,5,6};

auto c = a *append* b;

यहाँ मुख्य यह है कि हम एक append के प्रकार को परिभाषित करते हैं append_t:named_operator::make_operator<append_t>

फिर हम दाएं और बाएं प्रकार के लिए नाम_invoke (lhs, append_t, rhs) को ओवरलोड करते हैं।

लायब्रेरी ओवर lhs*append_t , एक अस्थायी half_apply ऑब्जेक्ट half_apply है। यह named_invoke( lhs, append_t, rhs ) को कॉल करने के लिए half_apply*rhs ओवरलोड half_apply*rhs

हमें बस उचित append_t टोकन बनाना होगा और उचित हस्ताक्षर के named_invoke अनुकूल named_invoke करना होगा, और सब कुछ हुक और काम करता है।

अधिक जटिल उदाहरण के लिए, मान लीजिए कि आप किसी std के तत्वों का तत्व-वार गुणन करना चाहते हैं :: सरणी:

template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
  return [](auto&& f) {
    return f( std::integral_constant<std::size_t, Is>{}... );
  };
}
template<std::size_t N>
auto indexer() { return indexer( std::make_index_sequence<N>{} ); }

namespace my_ns {
  struct e_times_t : named_operator::make_operator<e_times_t> {};
  constexpr e_times_t e_times{};

  template<class L, class R, std::size_t N,
    class Out=std::decay_t<decltype( std::declval<L const&>()*std::declval<R const&>() )>
  >
  std::array<Out, N> named_invoke( std::array<L, N> const& lhs, e_times_t, std::array<R, N> const& rhs ) {
    using result_type = std::array<Out, N>;
    auto index_over_N = indexer<N>();
    return index_over_N([&](auto...is)->result_type {
      return {{
        (lhs[is] * rhs[is])...
      }};
    });
  }
}

जीवंत उदाहरण

यह तत्व-वार सरणी कोड tuples या जोड़े या C- शैली सरणियों, या यहां तक कि चर लंबाई कंटेनरों पर काम करने के लिए बढ़ाया जा सकता है यदि आप तय करते हैं कि यदि लंबाई मेल नहीं खाती है तो क्या करें।

आप तत्व-वार ऑपरेटर प्रकार भी प्राप्त कर सकते हैं और lhs *element_wise<'+'>* rhs

*dot* और *cross* उत्पाद संचालक लिखना भी स्पष्ट उपयोग हैं।

अन्य का उपयोग करने के लिए * का उपयोग बढ़ाया जा सकता है, जैसे + । परिधि का नामांक संचालक नामांकित संचालक के पूर्वाभास को निर्धारित करता है, जो महत्वपूर्ण हो सकता है जब भौतिकी समीकरणों को C ++ पर अतिरिक्त () न्यूनतम उपयोग के साथ अनुवाद किया जाए।

ऊपर की लाइब्रेरी में थोड़े बदलाव के साथ, हम समर्थन कर सकते हैं ->*then* ऑपरेटर और std::function विस्तार करें std::function अपडेट किए जा रहे मानक से पहले std::function , या मोनडिक ->*bind* लिखें। यह भी एक स्टेटफुल नामित ऑपरेटर, जहां हम ध्यान से पारित हो सकता था Op अंतिम आह्वान कार्य करने के लिए नीचे, की अनुमति:

named_operator<'*'> append = [](auto lhs, auto&& rhs) {
  using std::begin; using std::end;
  lhs.insert( end(lhs), begin(rhs), end(rhs) );
  return std::move(lhs);
};

C ++ 17 में एक नामित कंटेनर-अपेंडिंग ऑपरेटर उत्पन्न करना।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow