कोई डिफ़ॉल्ट चाल-असाइनमेंट / चाल-निर्माणकर्ता क्यों नहीं?


89

मैं एक साधारण प्रोग्रामर हूं। मेरे वर्ग के सदस्य सबसे अधिक बार POD- प्रकार और STL- कंटेनर से बने होते हैं। इसके कारण मुझे शायद ही कभी असाइनमेंट ऑपरेटर्स या कॉपी कंस्ट्रक्टर्स को लिखना पड़े, क्योंकि ये डिफ़ॉल्ट रूप से लागू होते हैं।

इसे जोड़ें, अगर मैं std::moveवस्तुओं पर उपयोग नहीं कर सकता हूं, तो यह असाइनमेंट-ऑपरेटर का उपयोग करता है, जिसका अर्थ std::moveपूरी तरह से सुरक्षित है।

जैसा कि मैं एक साधारण प्रोग्रामर हूं, मैं हर क्लास में लिखने वाले एक मूव कंस्ट्रक्टर / असाइनमेंट ऑपरेटर को जोड़े बिना मूव-क्षमताओं का लाभ उठाना चाहता हूं, क्योंकि कंपाइलर बस उन्हें " this->member1_ = std::move(other.member1_);..." के रूप में लागू कर सकता है।

लेकिन यह नहीं है (कम से कम विज़ुअल 2010 में नहीं), क्या इसका कोई विशेष कारण है?

अधिक महत्वपूर्ण बात; क्या इसके आसपास पाने के लिए कोई रास्ता नहीं है?

अपडेट: यदि आप GManNickG के जवाब को देखते हैं तो वह इसके लिए एक शानदार मैक्रो प्रदान करता है। और यदि आप नहीं जानते, यदि आप चाल-शब्दार्थ को लागू करते हैं तो आप स्वैप सदस्य फ़ंक्शन को हटा सकते हैं।


5
तुम्हें पता है कि आप संकलक एक डिफ़ॉल्ट चाल ctor उत्पन्न कर सकते हैं
aaronman

3
std :: चाल एक चाल नहीं करता है, यह बस एक l-value से r-value करता है। कदम अभी भी कदम निर्माता द्वारा किया जाता है।
ओवेन डेलाहॉय

1
क्या आप बात कर रहे हैं MyClass::MyClass(Myclass &&) = default;?
सैंडबर्ग

हाँ, आजकल :)
विक्टर सेहर

जवाबों:


76

चाल निर्माणकर्ताओं और असाइनमेंट ऑपरेटरों की निहित पीढ़ी विवादास्पद रही है और सी ++ मानक के हाल के ड्राफ्ट में प्रमुख संशोधन हुए हैं, इसलिए वर्तमान में उपलब्ध संकलक संभवतः अंतर्निहित पीढ़ी के संबंध में अलग व्यवहार करेंगे।

मुद्दे के इतिहास के बारे में अधिक जानने के लिए, 2010 WG21 पेपर सूची देखें और "Mov" खोजें

वर्तमान विनिर्देश (N3225, नवंबर से) राज्यों (N3225 12.8 / 8):

यदि किसी वर्ग की परिभाषा Xस्पष्ट रूप से एक चाल निर्माणकर्ता की घोषणा नहीं करती है, तो किसी को केवल और केवल अगर डिफ़ॉल्ट रूप से घोषित किया जाएगा

  • X उपयोगकर्ता-घोषित प्रतिलिपि निर्माता नहीं है, और

  • X उपयोगकर्ता-घोषित कॉपी असाइनमेंट ऑपरेटर नहीं है,

  • X उपयोगकर्ता-घोषित चाल असाइनमेंट ऑपरेटर के पास नहीं है,

  • X उपयोगकर्ता-घोषित विध्वंसक नहीं है, और

  • चाल निर्माणकर्ता को हटाए गए रूप में परिभाषित नहीं किया जाएगा।

12.8 / 22 में समान भाषा है जब निर्दिष्ट असाइनमेंट ऑपरेटर को डिफ़ॉल्ट रूप से घोषित किया जाता है। आप N3203 में निहित चाल पीढ़ी के वर्तमान विनिर्देश का समर्थन करने के लिए किए गए परिवर्तनों की पूरी सूची पा सकते हैं : अंतर्निहित चाल उत्पन्न करने के लिए शर्तों को कसना , जो कि बड़े पैमाने पर Bjarne Stroustrup के पेपर 3232 द्वारा प्रस्तावित प्रस्तावों में से एक पर आधारित था : साथ सही चल रहा है


4
मैंने कुछ डायग्राम के साथ एक छोटा सा लेख लिखा है जिसमें निहित (चाल) निर्माण / असाइनमेंट के लिए संबंधों का वर्णन है: mmocny.wordpress.com/2010/12/09/implicit-move-wont-go
mmocny

जब भी मुझे पॉलीमॉर्फिक बेस क्लासेस में खाली डिस्ट्रक्टर्स को परिभाषित करना होता है, तो इसे वर्चुअल के रूप में निर्दिष्ट करने के लिए, मुझे स्पष्ट रूप से मूव कंस्ट्रक्टर और असाइनमेंट ऑपरेटर के रूप में अच्छी तरह से परिभाषित करना होगा :(।
someguy

@ जेम्स मैकनेलिस: यह कुछ ऐसा है जिसे मैंने पहले कोशिश की थी, लेकिन संकलक को यह पसंद नहीं आया। मैं इस उत्तर में त्रुटि संदेश पोस्ट करने जा रहा था, लेकिन त्रुटि को पुन: उत्पन्न करने की कोशिश करने के बाद, मुझे एहसास हुआ कि इसका उल्लेख है cannot be defaulted *in the class body*। तो, मैंने बाहर विध्वंसक को परिभाषित किया और यह काम किया :)। मुझे यह थोड़ा अजीब लगता है, हालाँकि। क्या किसी के पास स्पष्टीकरण है? कंपाइलर 4.6.1 है
someguy

3
हो सकता है कि अब हमें इस उत्तर का अपडेट मिल जाए कि C ++ 11 की पुष्टि हो गई है? जिज्ञासु ने क्या व्यवहार जीता।
जोसेफ गार्विन

2
@Guy Avraham: मुझे लगता है कि मैं जो कह रहा था (यह 7 साल हो गया है) यह है कि अगर मेरे पास एक उपयोगकर्ता-घोषित विध्वंसक (यहां तक ​​कि एक खाली आभासी है), तो कोई भी चाल निर्माणकर्ता को डिफ़ॉल्ट रूप से घोषित नहीं किया जाएगा। मुझे लगता है कि कॉपी शब्दार्थ में परिणाम होगा? (मैंने वर्षों में C ++ को नहीं छुआ है।) जेम्स मैकनेलिस ने टिप्पणी की कि virtual ~D() = default;काम करना चाहिए और फिर भी एक अंतर्निहित चाल निर्माता को अनुमति देना चाहिए।
कुछ

13

अवैध रूप से उत्पन्न चाल निर्माणकर्ताओं को मानक के लिए माना गया है, लेकिन खतरनाक हो सकता है। डेव अब्राहम का विश्लेषण देखें ।

हालांकि, अंत में, मानक में शामिल निर्माण निर्माताओं और स्थानांतरित असाइनमेंट ऑपरेटरों की निहित पीढ़ी शामिल थी, हालांकि सीमाओं की काफी पर्याप्त सूची के साथ:

यदि एक वर्ग X की परिभाषा स्पष्ट रूप से एक चाल निर्माणकर्ता की घोषणा नहीं करती है, तो एक को कथित रूप से डिफ़ॉल्ट के रूप में घोषित किया जाएगा यदि और केवल अगर
- X के पास उपयोगकर्ता-घोषित प्रतिलिपि निर्माता नहीं है,
- X के पास उपयोगकर्ता-घोषित प्रतिलिपि असाइनमेंट ऑपरेटर नहीं है ,
- X के पास उपयोगकर्ता-घोषित चाल असाइनमेंट ऑपरेटर नहीं है,
- X में उपयोगकर्ता-घोषित विध्वंसक नहीं है, और
- चाल निर्माणकर्ता को हटाए गए रूप में परिभाषित नहीं किया जाएगा।

हालांकि यह कहानी के लिए बिल्कुल नहीं है। एक ctor घोषित किया जा सकता है, लेकिन फिर भी हटाए जाने के रूप में परिभाषित किया गया है:

अनुमानित रूप से घोषित कॉपी / मूव कंस्ट्रक्टर अपने वर्ग का एक इनलाइन सार्वजनिक सदस्य है। एक X के लिए एक डिफ़ॉल्ट कॉपी / मूव कंस्ट्रक्टर को X के रूप में परिभाषित किया गया है, यदि X है:

- एक गैर-तुच्छ संगत निर्माता और X के साथ एक भिन्न सदस्य एक संघ-जैसा वर्ग है,
- वर्ग प्रकार M (या उसके बाद) का एक गैर-स्थैतिक डेटा सदस्य जिसे कॉपी नहीं किया जा सकता है / स्थानांतरित किया जा सकता है क्योंकि अधिभार संकल्प (13.3), के रूप में एम के संगत कंस्ट्रक्टर पर लागू होता है, एक अस्पष्टता या एक फ़ंक्शन का परिणाम होता है जो कि डिफॉल्ट किए गए कंस्ट्रक्टर से हटा दिया जाता है या दुर्गम होता है,
- एक प्रत्यक्ष या वर्चुअल बेस क्लास बी जिसे कॉपी नहीं किया जा सकता है / स्थानांतरित किया जा सकता है क्योंकि बी के संबंधित कंस्ट्रक्टर पर लागू होने के कारण अधिभार संकल्प (13.3)। , एक अस्पष्टता या एक फ़ंक्शन में परिणाम होता है जो कि डिफॉल्ट किए गए निर्माता से हटा दिया जाता है या दुर्गम होता है,
- किसी भी प्रत्यक्ष या वर्चुअल बेस क्लास या एक प्रकार का गैर-स्थैतिक डेटा सदस्य जो डिस्ट्रक्टर के साथ होता है, जिसे डिफॉल्ट किए गए कंस्ट्रक्टर से हटा दिया जाता है या दुर्गम होता है,
- कॉपी कंस्ट्रक्टर के लिए, एक गैर-स्टेटिक डेटा मेंबर ऑफ रेवल्यू रेफरेंस टाइप, या
- मूव कंस्ट्रक्टर के लिए, एक नॉन-स्टैटिक डेटा मेंबर या डायरेक्ट या वर्चुअल बेस क्लास, जिसमें एक मूव कंस्ट्रक्टर नहीं होता है और ट्राइवली नहीं होता है copyable।


वर्तमान कामकाजी मसौदा कुछ शर्तों के तहत अंतर्निहित चाल पीढ़ी की अनुमति देता है और मुझे लगता है कि संकल्प बड़े पैमाने पर अब्राहम की चिंताओं को संबोधित करता है।
जेम्स मैकनेलिस

मुझे यकीन नहीं है कि मुझे समझ आया कि ट्वीक 2 और टीक 3 के बीच उदाहरण में कौन सी चाल टूट सकती है। क्या आप इसे समझा सकते हैं?
मत्तीउह एम।

@ माथिउ एम।: ट्वीक 2 और ट्वीक 3 दोनों टूट गए हैं, और वास्तव में बहुत समान तरीके से। Tweak 2 में, निजी सदस्य होते हैं, जो चाल ctor द्वारा टूट सकते हैं। Tweak 3 में, वर्ग निजी सदस्यों की जरूरत नहीं है अपने आप में है, लेकिन यह निजी वंशानुक्रम, सार्वजनिक और आधार के संरक्षित सदस्यों व्युत्पन्न की निजी सदस्यों, एक ही समस्या के लिए अग्रणी बनने का उपयोग करता है के बाद से।
जेरी कॉफिन

1
मुझे वास्तव में समझ में नहीं आया कि चाल निर्माणकर्ता वर्ग को किस तरह से तोड़ देगा Tweak2। मुझे लगता है कि यह इस तथ्य के साथ कुछ करने के लिए मिला है कि Numberस्थानांतरित किया vectorजाएगा और इसकी प्रतिलिपि बनाई जाएगी ... लेकिन मुझे यकीन नहीं है: / मुझे समझ में नहीं आता कि समस्या का सामना करना पड़ेगा Tweak3
मथिउ एम।

आपके द्वारा दिया गया लिंक मृत प्रतीत हो रहा है?
वुल्फ

8

(अभी के लिए, मैं एक बेवकूफ मैक्रो पर काम कर रहा हूँ ...)

हाँ, मैं वह मार्ग भी गया था। यहाँ आपका मैक्रो है:

// detail/move_default.hpp
#ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP
#define UTILITY_DETAIL_MOVE_DEFAULT_HPP

#include <boost/preprocessor.hpp>

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE(pR, pData, pBase) pBase(std::move(pOther))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE(pR, pData, pBase) pBase::operator=(std::move(pOther));

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR(pR, pData, pMember) pMember(std::move(pOther.pMember))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT(pR, pData, pMember) pMember = std::move(pOther.pMember);

#define UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        ,                                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)                                                   \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#endif

// move_default.hpp
#ifndef UTILITY_MOVE_DEFAULT_HPP
#define UTILITY_MOVE_DEFAULT_HPP

#include "utility/detail/move_default.hpp"

// move bases and members
#define UTILITY_MOVE_DEFAULT(pT, pBases, pMembers) UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)

// base only version
#define UTILITY_MOVE_DEFAULT_BASES(pT, pBases) UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)

// member only version
#define UTILITY_MOVE_DEFAULT_MEMBERS(pT, pMembers) UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)

#endif

(मैंने वास्तविक टिप्पणियों को हटा दिया है, जो लंबाई और वृत्तचित्र हैं।)

आप अपनी कक्षा में आधार और / या सदस्यों को एक पूर्व-सूची सूची के रूप में निर्दिष्ट करते हैं, उदाहरण के लिए:

#include "move_default.hpp"

struct foo
{
    UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str));

    int x;
    std::string str;
};

struct bar : foo, baz
{
    UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz));
};

struct baz : bar
{
    UTILITY_MOVE_DEFAULT(baz, (bar), (ptr));

    void* ptr;
};

और एक मूव-कंस्ट्रक्टर और मूव-असाइनमेंट ऑपरेटर आता है।

(एक तरफ के रूप में, अगर किसी को पता है कि मैं विवरण को एक मैक्रो में कैसे जोड़ सकता हूं, तो वह प्रफुल्लित होगा।)


आपका बहुत-बहुत धन्यवाद, मेरा भी समान है, सिवाय इसके कि मुझे एक तर्क के रूप में सदस्य चर की संख्या को पारित करना था (जो वास्तव में बेकार है)।
विक्टर सेहर

1
@Viktor: कोई बात नहीं। यदि बहुत देर नहीं हुई है, तो मुझे लगता है कि आपको स्वीकार किए गए अन्य उत्तरों में से एक को चिह्नित करना चाहिए। मेरा एक "अधिक, यहाँ एक तरह से" और अपने वास्तविक प्रश्न का उत्तर नहीं था।
GManNickG

1
अगर मैं आपके मैक्रो को सही तरीके से पढ़ रहा हूं, तो जैसे ही आपके कंपाइलर ने डिफॉल्ट मूव मेंबर्स को अप्लाई किया, वैसे ही आपके ऊपर के उदाहरण नॉन-कॉपीबल हो जाएंगे। स्पष्ट रूप से घोषित चाल सदस्यों के उपस्थित होने पर प्रतिलिपि सदस्यों की अवैध पीढ़ी को रोक दिया जाता है।
हावर्ड हिनान्ट

@ हॉवर्ड: यह ठीक है, यह तब तक एक अस्थायी समाधान है। :)
GManNickG

GMan: यदि आप एक स्वैप कार्य करते हैं तो यह मैक्रो मूव-कंस्ट्रक्टर \ असाइन करता है:
विक्टर सेहर

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