C ++ F को कॉल करने के लिए F <T> :: Foo (T &&) में कटौती क्यों नहीं कर सकता?


9

निम्नलिखित टेम्पलेट संरचना को देखते हुए:

template<typename T>
struct Foo {
    Foo(T&&) {}
};

यह संकलित करता है, और होने के Tलिए घटाया जाता है int:

auto f = Foo(2);

लेकिन यह संकलन नहीं करता है: https://godbolt.org/z/hAA9TE

int x = 2;
auto f = Foo(x);

/*
<source>:12:15: error: no viable constructor or deduction guide for deduction of template arguments of 'Foo'
    auto f = Foo(x);
             ^

<source>:7:5: note: candidate function [with T = int] not viable: no known conversion from 'int' to 'int &&' for 1st argument
    Foo(T&&) {}
    ^
*/

हालाँकि, Foo<int&>(x)स्वीकार किया जाता है।

लेकिन जब मैं एक प्रतीत होता है निरर्थक उपयोगकर्ता परिभाषित कटौती गाइड, यह काम करता है:

template<typename T>
Foo(T&&) -> Foo<T>;

उपयोगकर्ता-परिभाषित कटौती मार्गदर्शिका के बिना कटौती क्यों नहीं की Tजा सकती है int&?



यह प्रश्न टेम्पलेट प्रकारों के बारे में लगता है जैसेFoo<T<A>>
jtbandes

जवाबों:


5

मुझे लगता है कि यहां भ्रम पैदा होता है क्योंकि अग्रेषण संदर्भों के बारे में संश्लेषित कटौती गाइड के लिए एक विशिष्ट अपवाद है।

यह सही है कि निर्माणकर्ता से उत्पन्न वर्ग टेम्पलेट तर्क कटौती के उद्देश्य और उपयोगकर्ता द्वारा परिभाषित कटौती गाइड से उत्पन्न उम्मीदवार बिल्कुल एक जैसे हैं, यानी:

template<typename T>
auto f(T&&) -> Foo<T>;

लेकिन कंस्ट्रक्टर से उत्पन्न एक के लिए, T&&एक साधारण प्रतिद्वंद्विता संदर्भ है, जबकि यह उपयोगकर्ता-परिभाषित मामले में एक अग्रेषण संदर्भ है। यह C ++ 17 मानक के [temp.deduct.call] / 3 द्वारा निर्दिष्ट है (ड्राफ्ट N4659, मेरा जोर दें):

एक फ़ॉरवर्डिंग संदर्भ एक cv-unqualified टेम्पलेट पैरामीटर का एक रेवल्यू संदर्भ होता है जो क्लास टेम्पलेट के टेम्पलेट पैरामीटर (क्लास टेम्पलेट तर्क कटौती ([over.match.class.deduct)) के दौरान) का प्रतिनिधित्व नहीं करता है

इसलिए, क्लास कंस्ट्रक्टर से संश्लेषित उम्मीदवार Tआगे के संदर्भ से नहीं घटाएगा (जो Tकि एक लैवल्यू संदर्भ हो सकता है, ताकि वह T&&भी एक रेवेले रेफरेंस हो), लेकिन इसके बजाय केवल Tगैर-संदर्भ के रूप में कटौती करेगा , इसलिए वह T&&हमेशा होता है एक संदर्भ संदर्भ।


1
स्पष्ट और संक्षिप्त उत्तर के लिए धन्यवाद। क्या आप जानते हैं कि संदर्भों को अग्रेषित करने के नियमों में वर्ग टेम्पलेट मापदंडों के लिए अपवाद क्यों है?
jtbandes

2
@jtbandes अमेरिकी राष्ट्रीय निकाय की एक टिप्पणी के परिणामस्वरूप इसे बदल दिया गया है, पेपर p0512r0 देखें । मुझे हालांकि टिप्पणी नहीं मिली। तर्क के लिए मेरा अनुमान है कि अगर आप एक निर्माता एक rvalue संदर्भ लेने लिखते हैं, आप आम तौर पर यह उसी तरह है कि क्या आप एक निर्दिष्ट काम करने की उम्मीद है Foo<int>(...)या सिर्फ Foo(...)जो अग्रेषण संदर्भ के साथ मामले (जो अनुमान सकता है नहीं है, Foo<int&>। बजाय
अखरोट

6

यहाँ मुद्दा यह है कि, चूंकि वर्ग पर रोक लगाई गई है T, निर्माता में Foo(T&&)हम प्रकार कटौती नहीं कर रहे हैं ; हमारे पास हमेशा एक आर-मूल्य संदर्भ होता है। यही है, Fooवास्तव में इस तरह के लिए निर्माता दिखता है:

Foo(int&&)

Foo(2)काम करता है क्योंकि 2एक प्रचलन है।

Foo(x)ऐसा नहीं है क्योंकि xएक अंतराल है जो बांध नहीं सकता है int&&। आप std::move(x)इसे उपयुक्त प्रकार ( डेमो ) में डालने के लिए कर सकते हैं

Foo<int&>(x)ठीक काम करता है क्योंकि Foo(int&)संदर्भ ढहने के नियमों के कारण निर्माणकर्ता बन जाता है; शुरू में यह मानक के अनुसार Foo((int&)&&)ढह जाता है Foo(int&)

आपके "निरर्थक" कटौती गाइड के संबंध में: प्रारंभ में कोड के लिए एक डिफ़ॉल्ट टेम्पलेट कटौती गाइड है जो मूल रूप से एक हेल्पर फ़ंक्शन की तरह काम करता है:

template<typename T>
struct Foo {
    Foo(T&&) {}
};

template<typename T>
Foo<T> MakeFoo(std::add_rvalue_reference_t<T> value)
{
   return Foo<T>(std::move(value));
}

//... 
auto f = MakeFoo(x);

ऐसा इसलिए है क्योंकि मानक यह तय करता है कि इस (काल्पनिक) टेम्पलेट विधि में कक्षा के समान टेम्पलेट पैरामीटर हैं (बस T) किसी भी टेम्पलेट पैरामीटर के बाद निर्माणकर्ता के रूप में (इस मामले में कोई नहीं; निर्माणकर्ता अस्थायी नहीं है)। फिर, फ़ंक्शन पैरामीटर के प्रकार कंस्ट्रक्टर में समान हैं। हमारे मामले में, इंस्टेंटिअटिंग के बाद Foo<int>, कंस्ट्रक्टर जैसा दिखता है Foo(int&&), दूसरे शब्दों में एक प्रतिद्वंद्विता-संदर्भ। इसलिए add_rvalue_reference_tऊपर का उपयोग ।

जाहिर है यह काम नहीं करता है।

जब आपने अपना "अनावश्यक" कटौती गाइड जोड़ा:

template<typename T>
Foo(T&&) -> Foo<T>;

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

अब हम एक और (काल्पनिक) हेल्पर फंक्शन उत्पन्न करते हैं जो इस तरह दिखता है:

template<class U>
Foo<U> MakeFoo(U&& u)
{
   return Foo<U>(std::forward<U>(u));
}

// ...
auto f = MakeFoo(x);

(निर्माणकर्ता के लिए हमारी कॉल को क्लास टेम्पलेट तर्क कटौती के उद्देश्यों के लिए सहायक समारोह में पुनर्निर्देशित किया Foo(x)जाता है , इसलिए MakeFoo(x))।

यह बस U&&बनने int&और बनने की अनुमति देता हैTint


टेम्प्लेटिंग का दूसरा स्तर आवश्यक नहीं लगता है; क्या मूल्य प्रदान करता है? क्या आप कुछ प्रलेखन के लिए एक लिंक प्रदान कर सकते हैं जो स्पष्ट करता है कि T && को हमेशा यहाँ एक संदर्भ के रूप में क्यों माना जाता है?
jtbandes

1
लेकिन अगर टी अभी तक नहीं काटा गया है, तो "टी के लिए किसी भी प्रकार के परिवर्तनीय" का क्या मतलब है?
jtbandes

1
आप वर्कअराउंड की पेशकश करने के लिए जल्दी हैं, लेकिन क्या आप इस कारण से स्पष्टीकरण पर अधिक ध्यान केंद्रित कर सकते हैं कि यह तब तक काम क्यों नहीं करता जब तक कि आप इसे किसी तरह से संशोधित नहीं करते? यह काम क्यों नहीं करता है? " xएक अंतराल है जो बांध नहीं सकता है int&&" लेकिन जो कोई नहीं समझता है वह हैरान हो जाएगा जो Foo<int&>(x)काम कर सकता है लेकिन अपने आप पता नहीं लगाया गया था - मुझे लगता है कि हम सभी क्यों की गहरी समझ चाहते हैं।
वीक

2
@Wyck: मैंने पोस्ट को अपडेट किया है कि क्यों इस पर अधिक ध्यान केंद्रित किया जाए।
एंडीजी

3
@ असली कारण बहुत सरल है: विशेष रूप से आश्चर्य को रोकने के लिए संश्लेषित कटौती गाइड में मानक विशेष रूप से पूर्ण अग्रेषण शब्दार्थ को बंद कर दिया
एलएफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.