मैं एक संस्करण के सूचकांक को पुनः प्राप्त क्यों नहीं कर सकता हूं और इसकी सामग्री प्राप्त करने के लिए इसका उपयोग कर सकता हूं?


10

मैं एक प्रकार की सामग्री तक पहुँचने का प्रयास कर रहा हूँ। मुझे नहीं पता कि वहाँ क्या है, लेकिन शुक्र है कि वैरिएंट है। इसलिए मैंने सोचा कि मैं केवल वेरिएंट से पूछूंगा कि यह किस इंडेक्स पर है और फिर उस इंडेक्स का उपयोग std::getउसकी सामग्री के लिए करें।

लेकिन यह संकलन नहीं है:

#include <variant>

int main()
{
  std::variant<int, float, char> var { 42.0F };

  const std::size_t idx = var.index();

  auto res = std::get<idx>(var);

  return 0;
}

त्रुटि std::getकॉल में होती है:

error: no matching function for call to get<idx>(std::variant<int, float, char>&)’
   auto res = std::get<idx>(var);
                               ^
In file included from /usr/include/c++/8/variant:37,
                 from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
     get(std::pair<_Tp1, _Tp2>& __in) noexcept
     ^~~
/usr/include/c++/8/utility:216:5: note:   template argument deduction/substitution failed:
main.cpp:9:31: error: the value of idx is not usable in a constant expression
   auto res = std::get<idx>(var);
                               ^
main.cpp:7:15: note: std::size_t idx is not const
   std::size_t idx = var.index();
               ^~~

मैं इसे कैसे ठीक करूं?


3
मुझे संदेह है कि आपको जो त्रुटि मिल रही है वह सूचकांक से संबंधित है एक स्थिर अभिव्यक्ति नहीं है। कृपया कंपाइलर त्रुटि संदेश पोस्ट करें ताकि हम सार्थक सहायता प्रदान कर सकें।
patatahooligan

अड़चन नहीं है?
रेलेह

ओह! आपने त्रुटि के बारे में बात की थी, लेकिन आपने त्रुटि का सटीक पाठ पोस्ट नहीं किया।
जोनाथन वुड

1
चूक के लिए खेद है, मैंने प्रश्न को अद्यतन किया
एलेक्स

जवाबों:


4

अनिवार्य रूप से, आप नहीं कर सकते।

आप ने लिखा:

मुझे नहीं पता कि वहां क्या है, लेकिन शुक्र है कि वैरिएंट है

... लेकिन केवल रन-टाइम पर, संकलन-समय पर नहीं।
और इसका मतलब है कि आपका idxमूल्य संकलन-समय नहीं है।
और इसका मतलब है कि आप get<idx>()सीधे उपयोग नहीं कर सकते ।

कुछ आप कर सकते हैं एक स्विच स्टेटमेंट है; बदसूरत, लेकिन यह काम करेगा:

switch(idx) {
case 0: { /* code which knows at compile time that idx is 0 */ } break;
case 1: { /* code which knows at compile time that idx is 1 */ } break;
// etc. etc.
}

हालांकि यह बदसूरत है। जैसा कि टिप्पणियों से पता चलता है, आप भी हो सकते हैं std::visit()(जो ऊपर दिए गए कोड से बहुत अलग नहीं है, सिवाय इसके कि यह स्पष्ट होने के बजाय वैरेडिक टेम्प्लेट तर्कों का उपयोग करके) और पूरी तरह से स्विच से बचें। अन्य सूचकांक आधारित दृष्टिकोणों के लिए (विशिष्ट नहीं std::variant), देखें:

रन-टाइम संख्यात्मक सांख्यिक मापदंडों का अनुकरण करने के लिए मुहावरे?


@ कैलेथ: हाँ। संपादित।
einpoklum

5

संकलक को काम करने के idxलिए संकलन समय के मूल्य को जानना होगा std::get<idx>(), क्योंकि इसका उपयोग टेम्पलेट तर्क के रूप में किया जा रहा है।

पहला विकल्प: यदि कोड को संकलन समय पर चलाने के लिए है, तो सब कुछ बनाएं constexpr:

constexpr std::variant<int, float, char> var { 42.0f };

constexpr std::size_t idx = var.index();

constexpr auto res = std::get<idx>(var);

यह काम करता है क्योंकि std::variantहै constexpr(इसके निर्माणकर्ता और तरीकों सब कर रहे हैं अनुकूल constexpr)।

दूसरा विकल्प: यदि कोड का संकलन समय पर चलने के लिए नहीं है, जो कि संभावना है, तो संकलक संकलन के समय में कटौती नहीं कर सकता है res, क्योंकि यह तीन अलग-अलग चीजें ( int, floatया char) हो सकती हैं। C ++ एक स्टेटिकली-टाइप की गई भाषा है, और कंपाइलर को auto res = ...उस एक्सप्रेशन से टाइप करने में सक्षम होना चाहिए , जो इस प्रकार है (यानी यह हमेशा एक ही प्रकार का होना चाहिए)।

आप std::get<T>इंडेक्स के बजाय टाइप के साथ उपयोग कर सकते हैं , यदि आप पहले से ही जानते हैं कि यह क्या होगा:

std::variant<int, float, char> var { 42.0f }; // chooses float

auto res = std::get<float>(var);

सामान्य तौर पर, std::holds_alternativeयह जांचने के लिए उपयोग करें कि क्या संस्करण दिए गए प्रत्येक प्रकार को पकड़े हुए है, और उन्हें अलग से संभालें:

std::variant<int, float, char> var { 42.0f };

if (std::holds_alternative<int>(var)) {
    auto int_res = std::get<int>(var); // int&
    // ...
} else if (std::holds_alternative<float>(var)) {
    auto float_res = std::get<float>(var); // float&
    // ...
} else {
    auto char_res = std::get<char>(var); // char&
    // ...
}

वैकल्पिक रूप से आप उपयोग कर सकते हैं std::visit। यह थोड़ा अधिक जटिल है: आप एक लंबो / टेम्प्लेटेड फ़ंक्शन का उपयोग कर सकते हैं जो टाइप-एग्नोस्टिक है और सभी प्रकार के प्रकारों के लिए काम करता है, या एक अधिभार कॉल ऑपरेटर के साथ एक फ़नकार पास करता है:

std::variant<int, float, char> var { 42.0f };

std::size_t idx = var.index();

std::visit([](auto&& val) {
    // use val, which may be int&, float& or char&
}, var);

स्टड देखें :: विवरण और उदाहरण के लिए यात्रा करें


3

समस्या यह है कि std::get<idx>(var);idx एक ज्ञात समय ज्ञात मूल्य के लिए (के लिए ) की आवश्यकता है ।

तो एक constexprमूल्य

// VVVVVVVVV
   constexpr std::size_t idx = var.index();

लेकिन idxजैसा कि शुरू करने के लिए constexprभी varहोना चाहिए थाconstexpr

// VVVVVVVVV
   constexpr std::variant<int, float, char> var { 42.0F };

... और एक विवस्नीय संस्करण बहुत भिन्न नहीं है।
डेविस हेरिंग

@DavisHerring - यह भी सच है।
अधिकतम ६६

2

समस्या संकलन-समय पर त्वरित किए जाने से उत्पन्न होती है, जबकि आपके द्वारा प्राप्त किए जा रहे सूचकांक की गणना रन-टाइम में की जाती है। इसी प्रकार, C ++ प्रकारों को भी संकलन-समय पर परिभाषित किया जाता है auto, यहां तक ​​कि घोषणा के साथ भी ,res कार्यक्रम के सुव्यवस्थित होने के लिए एक ठोस प्रकार होना चाहिए। इसका मतलब यह है कि टेम्पलेट पर प्रतिबंध के बिना भी, आप जो करने की कोशिश कर रहे हैं वह गैर-स्थिर अभिव्यक्ति के लिए स्वाभाविक रूप से असंभव है std::variant। इसके आसपास कोई कैसे काम करेगा?

सबसे पहले, यदि आपका संस्करण वास्तव में एक स्थिर अभिव्यक्ति है, तो कोड उम्मीद के मुताबिक काम करता है और संकलित करता है

#include <variant>

int main()
{
  constexpr std::variant<int, float, char> var { 42.0f };

  constexpr std::size_t idx = var.index();

  auto res = std::get<idx>(var);

  return 0;
}

अन्यथा आपको कुछ मैनुअल ब्रांचिंग तंत्र का उपयोग करना होगा

if (idx == 0) {
    // Now 'auto' will have a concrete type which I've explicitly used
    int value == std::get<0>(var);
}

आप विज़िटर पैटर्न का उपयोग करके इन शाखाओं को परिभाषित कर सकते हैं, std :: visit देखें


1

यह C ++ के मॉडल में स्वाभाविक रूप से असंभव है; विचार करें

template<class T> void f(T);
void g(std::variant<int,double> v) {
  auto x=std::get<v.index()>(v);
  f(x);
}

कौन सा fबुलाया जा रहा है, f<int>या f<double>? यदि यह "दोनों" है, तो इसका मतलब है कि gइसमें एक शाखा शामिल है (जो यह नहीं है), या कि इसके दो संस्करण हैं g(जो कि इसके कॉलर पर समस्या को बढ़ाता है)। और लगता f(T,U,V,W)है कि कंपाइलर बंद हो जाता है?

वास्तव में C ++ के लिए एक JIT का एक प्रस्ताव है जो इस तरह की चीजों को उन अतिरिक्त संस्करणों को संकलित करने की अनुमति देता है fजब उन्हें बुलाया जाता है, लेकिन यह बहुत जल्दी होता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.