C++
std :: वैकल्पिक
खोज…
परिचय
वैकल्पिक (जिन्हें शायद प्रकार के रूप में भी जाना जाता है) का उपयोग उस प्रकार का प्रतिनिधित्व करने के लिए किया जाता है जिसकी सामग्री मौजूद हो सकती है या नहीं भी हो सकती है। उन्हें C ++ 17 में std::optional
वर्ग के रूप में लागू किया गया है। उदाहरण के लिए, प्रकार std::optional<int>
की एक वस्तु std::optional<int>
में कुछ प्रकार के int
हो सकते हैं, या इसमें कोई मान नहीं हो सकता है।
वैकल्पिक रूप से या तो एक मूल्य का प्रतिनिधित्व करने के लिए उपयोग किया जाता है जो मौजूद नहीं हो सकता है या एक फ़ंक्शन से वापसी प्रकार के रूप में जो एक सार्थक परिणाम वापस करने में विफल हो सकता है।
वैकल्पिक के लिए अन्य दृष्टिकोण
समस्या को हल करने के लिए कई अन्य दृष्टिकोण हैं जो std::optional
हल करते हैं, लेकिन उनमें से कोई भी पूरा नहीं है: एक सूचक का उपयोग करना, एक प्रहरी का उपयोग करना, या एक pair<bool, T>
का उपयोग करना pair<bool, T>
।
वैकल्पिक बनाम सूचक
कुछ मामलों में, हम विफलता को इंगित करने के लिए किसी मौजूदा वस्तु या nullptr
को एक संकेतक प्रदान कर सकते हैं। लेकिन यह उन मामलों तक सीमित है जहां ऑब्जेक्ट पहले से मौजूद हैं - optional
, एक मूल्य प्रकार के रूप में, मेमोरी आवंटन का सहारा लिए बिना नई वस्तुओं को वापस करने के लिए भी उपयोग किया जा सकता है।
वैकल्पिक बनाम प्रहरी
एक सामान्य मुहावरा यह बताने के लिए विशेष मूल्य का उपयोग करना है कि मूल्य अर्थहीन है। यह अभिन्न प्रकार के लिए 0 या -1 हो सकता है, या संकेत के लिए nullptr
हो सकता है। हालांकि, यह मान्य मानों के स्थान को कम कर देता है (आप एक वैध 0 और एक व्यर्थ 0 के बीच अंतर नहीं कर सकते हैं) और कई प्रकारों में प्रहरी मूल्य के लिए एक प्राकृतिक विकल्प नहीं है।
वैकल्पिक बनाम std::pair<bool, T>
एक अन्य सामान्य मुहावरा है, एक जोड़ी प्रदान करना, जहाँ एक तत्व एक bool
जो यह दर्शाता है कि मूल्य सार्थक है या नहीं।
यह त्रुटि के मामले में मूल्य प्रकार को डिफ़ॉल्ट-रचनात्मक होने पर निर्भर करता है, जो कुछ प्रकारों के लिए संभव नहीं है और दूसरों के लिए संभव लेकिन अवांछनीय है। एक optional<T>
, त्रुटि के मामले में, कुछ भी निर्माण करने की आवश्यकता नहीं है।
मूल्य की अनुपस्थिति का प्रतिनिधित्व करने के लिए वैकल्पिक का उपयोग करना
C ++ 17 से पहले, nullptr
मान वाले nullptr
का nullptr
सामान्यतः एक मान के अभाव का प्रतिनिधित्व करता है। यह बड़ी वस्तुओं के लिए एक अच्छा समाधान है जिन्हें गतिशील रूप से आवंटित किया गया है और पहले से ही संकेत द्वारा प्रबंधित किया जाता है। हालांकि, यह समाधान छोटे या आदिम प्रकार जैसे कि int
लिए अच्छी तरह से काम नहीं करता है, जो शायद ही कभी गतिशील रूप से आवंटित या संकेत द्वारा प्रबंधित होते हैं। std::optional
इस सामान्य समस्या का एक व्यवहार्य समाधान प्रदान करता है।
इस उदाहरण में, struct Person
को परिभाषित किया गया है। किसी व्यक्ति के लिए पालतू जानवर रखना संभव है, लेकिन आवश्यक नहीं। इसलिए, Person
का pet
सदस्य एक std::optional
आवरण के साथ घोषित किया जाता है।
#include <iostream>
#include <optional>
#include <string>
struct Animal {
std::string name;
};
struct Person {
std::string name;
std::optional<Animal> pet;
};
int main() {
Person person;
person.name = "John";
if (person.pet) {
std::cout << person.name << "'s pet's name is " <<
person.pet->name << std::endl;
}
else {
std::cout << person.name << " is alone." << std::endl;
}
}
किसी फ़ंक्शन की विफलता का प्रतिनिधित्व करने के लिए वैकल्पिक का उपयोग करना
C ++ 17 से पहले, एक फ़ंक्शन आमतौर पर कई तरीकों में से एक में विफलता का प्रतिनिधित्व करता है:
- एक अशक्त सूचक लौटाया गया था।
- जैसे किसी फ़ंक्शन को कॉल करना
Delegate *App::get_delegate()
एकApp
इंस्टेंस पर जिसमें एक प्रतिनिधि नहीं था,nullptr
। - यह उन वस्तुओं के लिए एक अच्छा समाधान है जो गतिशील रूप से आवंटित किए गए हैं या बड़े हैं और पॉइंटर्स द्वारा प्रबंधित हैं, लेकिन छोटी वस्तुओं के लिए एक अच्छा समाधान नहीं है जो आमतौर पर स्टैक-आबंटित होते हैं और कॉपी करके पास होते हैं।
- जैसे किसी फ़ंक्शन को कॉल करना
- विफलता को इंगित करने के लिए रिटर्न प्रकार का एक विशिष्ट मूल्य आरक्षित था।
- उदाहरण के लिए, किसी फ़ंक्शन को
unsigned shortest_path_distance(Vertex a, Vertex b)
दो वर्टिकल पर जो जुड़ा नहीं है, इस तथ्य को इंगित करने के लिए शून्य वापस आ सकता है।
- उदाहरण के लिए, किसी फ़ंक्शन को
- मान को इंगित करने के लिए एक
bool
साथ जोड़ा गया था, लौटाया गया मान सार्थक था।- जैसे किसी फ़ंक्शन
std::pair<int, bool> parse(const std::string &str)
को एक स्ट्रिंग तर्क के साथ कहा जाता है जो पूर्णांक नहीं है एक जोड़ी को अनिर्धारितint
औरfalse
एकbool
सेट के साथ लौटाएगा।
- जैसे किसी फ़ंक्शन
इस उदाहरण में, जॉन को दो पालतू जानवर दिए गए हैं, फ़ुलफ़ी और फ़्यूरबॉल। फ़ंक्शन Person::pet_with_name()
को तब जॉन के पालतू मूंछ को पुनः प्राप्त करने के लिए बुलाया जाता है। चूंकि जॉन के पास व्हिस्कर्स नाम का एक पालतू जानवर नहीं है, इसलिए फ़ंक्शन विफल रहता है और इसके बजाय std::nullopt
वापस आ जाता है।
#include <iostream>
#include <optional>
#include <string>
#include <vector>
struct Animal {
std::string name;
};
struct Person {
std::string name;
std::vector<Animal> pets;
std::optional<Animal> pet_with_name(const std::string &name) {
for (const Animal &pet : pets) {
if (pet.name == name) {
return pet;
}
}
return std::nullopt;
}
};
int main() {
Person john;
john.name = "John";
Animal fluffy;
fluffy.name = "Fluffy";
john.pets.push_back(fluffy);
Animal furball;
furball.name = "Furball";
john.pets.push_back(furball);
std::optional<Animal> whiskers = john.pet_with_name("Whiskers");
if (whiskers) {
std::cout << "John has a pet named Whiskers." << std::endl;
}
else {
std::cout << "Whiskers must not belong to John." << std::endl;
}
}
वापसी मूल्य के रूप में वैकल्पिक
std::optional<float> divide(float a, float b) {
if (b!=0.f) return a/b;
return {};
}
यहां हम अंश a/b
वापस करते हैं, लेकिन यदि यह परिभाषित नहीं है (अनंत होगा) तो हम इसके बजाय खाली वैकल्पिक लौटाते हैं।
एक अधिक जटिल मामला:
template<class Range, class Pred>
auto find_if( Range&& r, Pred&& p ) {
using std::begin; using std::end;
auto b = begin(r), e = end(r);
auto r = std::find_if(b, e , p );
using iterator = decltype(r);
if (r==e)
return std::optional<iterator>();
return std::optional<iterator>(r);
}
template<class Range, class T>
auto find( Range&& r, T const& t ) {
return find_if( std::forward<Range>(r), [&t](auto&& x){return x==t;} );
}
find( some_range, 7 )
कंटेनर को खोजता है या नंबर 7
बराबर चीज़ के लिए some_range
को श्रेणी some_range
। find_if
यह एक विधेय के साथ करता है।
यह या तो एक खाली वैकल्पिक देता है अगर यह नहीं मिला, या एक वैकल्पिक इट्रेटर टोटे तत्व युक्त होता है अगर यह था।
यह आपको करने की अनुमति देता है:
if (find( vec, 7 )) {
// code
}
या और भी
if (auto oit = find( vec, 7 )) {
vec.erase(*oit);
}
पुनरावृत्तियों और परीक्षणों के साथ शुरुआत / समाप्ति के साथ गड़बड़ करने के बिना।
value_or
void print_name( std::ostream& os, std::optional<std::string> const& name ) {
std::cout "Name is: " << name.value_or("<name missing>") << '\n';
}
value_or
वैकल्पिक में संग्रहीत मान लौटाता है, या तर्क है कि अगर वहाँ कुछ भी नहीं है।
यह आपको शायद-शून्य वैकल्पिक लेने देता है और जब आपको वास्तव में एक मूल्य की आवश्यकता होती है तो एक डिफ़ॉल्ट व्यवहार देता है। इस तरह से करने से, "डिफ़ॉल्ट व्यवहार" निर्णय को उस बिंदु पर वापस धकेला जा सकता है, जहां यह सबसे अच्छा बनाया जाता है और तुरंत जरूरत होती है, बजाय कुछ इंजन के हिम्मत में कुछ डिफ़ॉल्ट मान उत्पन्न करने के।