2017-12-20 40 views
5

निम्नलिखित कोड:क्या std :: का व्यवहार rvalue-context tuples खतरनाक है?

#include <tuple> 

int main() 
{ 
    auto f = []() -> decltype (auto) 
    { 
    return std::get<0> (std::make_tuple (0)); 
    }; 

    return f(); 
} 

(चुपचाप) अपरिभाषित व्यवहार के साथ कोड उत्पन्न करता है - अस्थायी rvalue make_tuple द्वारा दिया std :: < मिल के माध्यम से प्रचारित> और है decltype (ऑटो) वापसी प्रकार पर के माध्यम से। तो यह एक अस्थायी के संदर्भ को वापस कर देता है जो दायरे से बाहर हो गया है। इसे यहां देखें https://godbolt.org/g/X1UhSw

अब, आप तर्क दे सकते हैं कि decltype(auto) का मेरा उपयोग गलती है। लेकिन मेरे जेनेरिक कोड में (जहां टुपल का प्रकार std::tuple<Foo &> हो सकता है) मैं हमेशा एक प्रतिलिपि बनाना नहीं चाहता हूं। मैं वास्तव में tuple से सटीक मूल्य या संदर्भ निकालना चाहता हूँ।

मेरे लग रहा है कि std::get के इस अधिभार खतरनाक है:

template< std::size_t I, class... Types > 
constexpr std::tuple_element_t<I, tuple<Types...> >&& 
get(tuple<Types...>&& t) noexcept; 

जबकि टपल तत्वों पर lvalue संदर्भ प्रचार शायद समझदार है, मुझे नहीं लगता कि rvalue संदर्भ के लिए रखती है।

मुझे यकीन है कि मानक समिति ने इसे बहुत सावधानी से सोचा था, लेकिन क्या कोई मुझे समझा सकता है कि इसे सबसे अच्छा विकल्प क्यों माना जाता था?

+0

* "मुझे नहीं लगता कि कि rvalue संदर्भ के लिए रखती है" * - अगर आप 'forward_as_tuple' करता है। आप मूल्य श्रेणी संरक्षित करना चाहते हैं। या यदि आप फ़ंक्शन रिटर्न वैल्यू से एक मान निकालना चाहते हैं। – StoryTeller

+0

धन्यवाद, लेकिन मैं वास्तव में लगता है कि यह मूल्य वर्ग संरक्षण है नहीं कर रहा हूँ। उदाहरण के लिए, अगर मैं :: एक 'एसटीडी वाला फ़ंक्शन टपल है' और मैं 'std :: <0> मिल' इस पर मैं एक सादे 'int', न कि एक' पूर्णांक प्राप्त करने के लिए उम्मीद करेंगे फोन && '। क्या आप मुझे एक ठोस उदाहरण दे सकते हैं जहां आप कुछ और चाहते हैं? –

+1

'प्रभावी संरचित बाइंडिंग के लिए आवश्यक get' के इस अधिभार नहीं है? –

उत्तर

2

निम्न उदाहरण पर विचार करें:

void consume(foo&&); 

template <typename Tuple> 
void consume_tuple_first(Tuple&& t) 
{ 
    consume(std::get<0>(std::forward<Tuple>(t))); 
} 

int main() 
{ 
    consume_tuple_first(std::tuple{foo{}}); 
} 

इस मामले में, हम जानते हैं कि std::tuple{foo{}} एक अस्थायी है और यह consume_tuple_first(std::tuple{foo{}}) अभिव्यक्ति की पूरी अवधि के लिए रहना होगा कि।

हम किसी भी अनावश्यक प्रतिलिपि से बचना चाहते हैं और आगे बढ़ना चाहते हैं, लेकिन अभी भी foo{} की अस्थायीता consume पर प्रसारित करना चाहते हैं।

कि ऐसा करने का एक ही तरीका है std::get वापसी एक rvalue संदर्भ जब एक अस्थायी std::tuple उदाहरण के साथ आमंत्रण होने से है।

live example on wandbox


std::get<0>(std::forward<Tuple>(t)) बदलने std::get<0>(t) करने के लिए एक संकलन त्रुटि (अपेक्षित रूप से) (on wandbox) पैदा करता है।


एक get विकल्प है कि एक अतिरिक्त अनावश्यक चाल में मूल्य परिणामों से रिटर्न के बाद:

template <typename Tuple> 
auto myget(Tuple&& t) 
{ 
    return std::get<0>(std::forward<Tuple>(t)); 
} 

template <typename Tuple> 
void consume_tuple_first(Tuple&& t) 
{ 
    consume(myget(std::forward<Tuple>(t))); 
} 

live example on wandbox


लेकिन क्यों यह माना जाता था किसी को भी मुझे समझा सकते हैं सर्वोत्तम विकल्प?

क्योंकि यह वैकल्पिक सामान्य कोड है कि मूल temporaries rvalue संदर्भ जब tuples तक पहुँचने से प्रसारित सक्षम बनाता है। मूल्य से लौटने का विकल्प अनावश्यक चाल संचालन के परिणामस्वरूप हो सकता है।

+0

आह मैं देखता हूँ। तो मेरा "सुरक्षित_get" जो अनजान प्रकार देता है, एक अतिरिक्त कदम का कारण बनता है। IMHO है कि एक कीमत सुरक्षा के लिए भुगतान करने के लायक हो जाएगा, लेकिन मैं कम से कम देख सकते हैं यह बहस का मुद्दा है। आपके उत्तर के लिए बहुत धन्यवाद! –

संबंधित मुद्दे