C++ में पीछे की जानकारी का प्रवाह कब होता है?




types language-lawyer (3)

मैं अधिभारित कार्यों के स्थैतिक कास्टिंग में विश्वास करता हूं कि प्रवाह सामान्य ओवरलोड रिज़ॉल्यूशन के विपरीत विपरीत दिशा में जाता है। तो उनमें से एक पीछे की तरफ है, मुझे लगता है।

मैंने CppCon 2018 में "क्लास टेम्पलेट आर्ग्यूमेंट CppCon 2018 " पर स्टीफन टी। लववेज की बात देखी, जहां कुछ समय पर वह आकस्मिक रूप से कहता है:

सी ++ प्रकार की जानकारी में लगभग कभी पीछे नहीं बहती ... मुझे "लगभग" कहना था क्योंकि एक या दो मामले हैं, संभवतः अधिक लेकिन बहुत कम

यह पता लगाने के बावजूद कि वह किस मामले का जिक्र कर रहा है, मैं कुछ भी नहीं कर सका। इसलिए सवाल:

किस मामले में सी ++ 17 मानक जनादेश देता है जो प्रकार की जानकारी पीछे की तरफ फैलता है?


यहां कम से कम एक मामला है:

struct foo {
  template<class T>
  operator T() const {
    std::cout << sizeof(T) << "\n";
    return {};
  }
};

यदि आप foo f; int x = f; double y = f; foo f; int x = f; double y = f; , operator T में T क्या है, यह जानने के लिए जानकारी टाइप करें "पिछड़ा"।

आप इसे अधिक उन्नत तरीके से उपयोग कर सकते हैं:

template<class T>
struct tag_t {using type=T;};

template<class F>
struct deduce_return_t {
  F f;
  template<class T>
  operator T()&&{ return std::forward<F>(f)(tag_t<T>{}); }
};
template<class F>
deduce_return_t(F&&)->deduce_return_t<F>;

template<class...Args>
auto construct_from( Args&&... args ) {
  return deduce_return_t{ [&](auto ret){
    using R=typename decltype(ret)::type;
    return R{ std::forward<Args>(args)... };
  }};
}

तो अब मैं कर सकता हूँ

std::vector<int> v = construct_from( 1, 2, 3 );

और यह काम करता है।

बेशक, क्यों न सिर्फ {1,2,3} ? खैर, {1,2,3} अभिव्यक्ति नहीं है।

std::vector<std::vector<int>> v;
v.emplace_back( construct_from(1,2,3) );

जो, स्वीकार्य रूप से, थोड़ा और जादूगर की आवश्यकता है: लाइव उदाहरण । (मुझे कटौती वापसी एफ की एक SFINAE जांच करने के लिए है, फिर एफ को SFINAE अनुकूल बनाएं, और मुझे deduce_return_t ऑपरेटर टी में std :: startizer_list को अवरुद्ध करना होगा।)


स्टीफन टी। लववेज ने उस मामले को समझाया जो वह एक ट्वीट में बात कर रहा था :

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

हम अधिभारित फ़ंक्शन के पते पर cppreference पेज से इसका उदाहरण देख सकते हैं, मैंने नीचे कुछ को छोड़ दिया है:

int f(int) { return 1; } 
int f(double) { return 2; }   

void g( int(&f1)(int), int(*f2)(double) ) {}

int main(){
    g(f, f); // selects int f(int) for the 1st argument
             // and int f(double) for the second

     auto foo = []() -> int (*)(int) {
        return f; // selects int f(int)
    }; 

    auto p = static_cast<int(*)(int)>(f); // selects int f(int)
}

माइकल पार्क कहते हैं :

यह एक ठोस प्रकार शुरू करने तक सीमित नहीं है, या तो। यह सिर्फ तर्कों की संख्या से अनुमान लगा सकता है

और यह लाइव उदाहरण प्रदान करता है:

void overload(int, int) {}
void overload(int, int, int) {}

template <typename T1, typename T2,
          typename A1, typename A2>
void f(void (*)(T1, T2), A1&&, A2&&) {}

template <typename T1, typename T2, typename T3,
          typename A1, typename A2, typename A3>
void f(void (*)(T1, T2, T3), A1&&, A2&&, A3&&) {}

int main () {
  f(&overload, 1, 2);
}

जो मैं यहां थोड़ा और विस्तार से बताता हूं।





type-deduction