কেন কোনও ডিফল্ট মুভ-অ্যাসাইনমেন্ট / মুভ-কনস্ট্রাক্টর নেই?


89

আমি একজন সাধারণ প্রোগ্রামার। আমার শ্রেণীর সদস্যদের ভেরিয়েবলগুলি প্রায়শই POD- প্রকার এবং এসটিএল-পাত্রে থাকে। এ কারণে আমাকে খুব কমই অ্যাসাইনমেন্ট অপারেটর বা অনুলিপি নির্মাণকারী লিখতে হয়, কারণ এগুলি ডিফল্টরূপে প্রয়োগ করা হয়।

এটি যুক্ত করুন, আমি যদি std::moveঅস্থাবর বস্তুগুলিতে ব্যবহার করি তবে এটি অ্যাসাইনমেন্ট-অপারেটরটি ব্যবহার করে, অর্থ std::moveপুরোপুরি নিরাপদ।

আমি যেহেতু একটি সাধারণ প্রোগ্রামার, আমি লিখছি প্রতি ক্লাসে একটি মুভ কনস্ট্রাক্টর / অ্যাসাইনমেন্ট অপারেটর যুক্ত না করেই মুভ-সক্ষমতার সুবিধা নিতে চাই, কারণ সংকলকটি কেবল তাদের হিসাবে প্রয়োগ করতে পারে " this->member1_ = std::move(other.member1_);..."

তবে এটি (কমপক্ষে ভিজ্যুয়াল ২০১০ এ নয়) এর কোনও বিশেষ কারণ আছে কি না?

অধিক গুরুত্বের সাথে; এই সমস্যা এড়ানোর পেতে কোন উপায় আছে কি?

আপডেট: আপনি GManNickG এর উত্তরটি নীচে দেখেন তবে তিনি এর জন্য দুর্দান্ত ম্যাক্রো সরবরাহ করেন। এবং যদি আপনি না জানতেন, আপনি যদি মুভ-শব্দার্থক প্রয়োগ করেন তবে আপনি অদলবদলের ফাংশনটি সরাতে পারবেন।


4
আপনি কি জানেন যে আপনি সংকলকটি একটি ডিফল্ট মুভ কর্টর তৈরি করতে পারেন
অ্যারোনম্যান

4
std :: সরানো একটি পদক্ষেপ সম্পাদন করে না, এটি কেবলমাত্র একটি এল-মান থেকে আর-মানে ক্যাসেট করে। সরানো এখনও মুভ কনস্ট্রাক্টর দ্বারা সঞ্চালিত হয়।
দেলোহয়

4
তুমি কি কথা বলছ MyClass::MyClass(Myclass &&) = default;?
স্যান্ডবার্গ

হ্যাঁ, আজকাল :)
ভিক্টর সেহর

উত্তর:


76

মুভ কনস্ট্রাক্টর এবং অ্যাসাইনমেন্ট অপারেটরগুলির অন্তর্নিহিত জেনারেশনটি বিতর্কযোগ্য ছিল এবং সি ++ স্ট্যান্ডার্ডের সাম্প্রতিক খসড়াগুলিতে বড় ধরনের সংশোধন হয়েছে, সুতরাং বর্তমানে উপলব্ধ সংকলকগুলি অন্তর্নিহিত প্রজন্মের ক্ষেত্রে পৃথকভাবে আচরণ করবে।

ইস্যুর ইতিহাস সম্পর্কে আরও তথ্যের জন্য, ২০১০ এর ডাব্লুজি 21 পত্রিকার তালিকাটি দেখুন এবং " মুভি " অনুসন্ধান করুন

বর্তমান স্পেসিফিকেশন (নভেম্বর থেকে N3225) জানিয়েছে (N3225 12.8 / 8):

যদি কোনও শ্রেণীর সংজ্ঞা Xস্পষ্টভাবে কোনও মুভ কনস্ট্রাক্টরকে ঘোষণা না করে তবে একটি স্পষ্টতই খেলাপী হিসাবে ঘোষণা করা হবে যদি এবং কেবল যদি

  • X কোনও ব্যবহারকারী-ঘোষিত অনুলিপি নির্মাণকারী নেই এবং

  • X কোনও ব্যবহারকারী-ঘোষিত অনুলিপি অপারেটর নেই

  • X কোনও ব্যবহারকারী-ঘোষিত মুভ অ্যাসাইনমেন্ট অপারেটর নেই,

  • X ব্যবহারকারীর দ্বারা ঘোষিত ডেস্ট্রাক্টর নেই এবং

  • মুভ কনস্ট্রাক্টর মুছে ফেলা হিসাবে স্পষ্টভাবে সংজ্ঞায়িত করা হবে না।

মুভ অ্যাসাইনমেন্ট অপারেটরকে স্পষ্টতই খেলাপি হিসাবে ঘোষণা করা হলে নির্দিষ্ট করে 12.8 / 22 তে একই ভাষা রয়েছে। আপনি N3203 এ অন্তর্নিহিত পদক্ষেপের প্রজন্মের বর্তমান স্পেসিফিকেশনকে সমর্থন করার জন্য করা পরিবর্তনের সম্পূর্ণ তালিকাটি খুঁজে পেতে পারেন : অন্তর্নিহিত পদক্ষেপগুলি উত্পন্ন করার শর্তকে আরও শক্ত করে তোলা , যা মূলত বজর্ন স্ট্রস্ট্রপের পেপার এন 32012 এর প্রস্তাবিত রেজোলিউশনের একটির উপর ভিত্তি করে ছিল : ডানদিকে সরানো


4
আমি এখানে কিছু চিত্রের
mmocny

উঘ তাই যখনই আমি কেবল ভার্চুয়াল হিসাবে নির্দিষ্ট করার জন্য পলিমারফিক বেস ক্লাসে ফাঁকা ডেস্ট্রাক্টরকে সংজ্ঞায়িত করতে হয়, আমাকে
মুভি

@ জেমস ম্যাকনেলিস: এটি এমন কিছু যা আমি আগে চেষ্টা করেছি, তবে সংকলকটি এটি পছন্দ করবে বলে মনে হয় না। আমি এই খুব উত্তরে ত্রুটি বার্তা পোস্ট করতে যাচ্ছিলাম, কিন্তু ত্রুটিটি পুনরুত্পাদন করার চেষ্টা করার পরে, আমি বুঝতে পেরেছিলাম যে এটি উল্লেখ করেছে cannot be defaulted *in the class body*। সুতরাং, আমি বাইরে ধ্বংসকারীকে সংজ্ঞায়িত করেছি এবং এটি কাজ করেছে :)। যদিও আমি এটি কিছুটা অদ্ভুত বলে মনে করি। কারও কি ব্যাখ্যা আছে?
সংকলকটি জিসিসি

4
সম্ভবত আমরা এখনই এই উত্তরের একটি আপডেট পেতে পারি যে সি ++ 11 অনুমোদিত হয়েছে? কৌতূহল যা আচরণগুলি জিতেছে।
জোসেফ গারভিন 23

4
@ গুয় অভ্রাহাম: আমি মনে করি আমি যা বলছিলাম (এটি 7 বছর হয়ে গেছে) তা হ'ল যদি আমার কাছে ব্যবহারকারী দ্বারা ঘোষিত ডেস্ট্রাক্টর (এমনকি খালি ভার্চুয়ালও থাকে), কোনও চালক নির্মাণকারীর স্পষ্টতই খেলাপি হিসাবে ঘোষণা করা হবে না। আমি মনে করি এর ফলে কপি শব্দার্থবিজ্ঞানের ফলাফল হবে? (আমি বছরগুলিতে সি ++ স্পর্শ করিনি।) জেমস ম্যাকনেলিস তখন মন্তব্য করেছিলেন যে virtual ~D() = default;কাজ করা উচিত এবং এখনও একটি অন্তর্নিহিত পদক্ষেপ নির্মাণকারীকে অনুমতি দেওয়া উচিত allow
someguy

13

সুস্পষ্টভাবে উত্পন্ন মুভ কনস্ট্রাক্টরগুলি স্ট্যান্ডার্ডের জন্য বিবেচিত হয়েছে তবে এটি বিপজ্জনক হতে পারে। ডেভ আব্রাহামসের বিশ্লেষণ দেখুন

তবে শেষ পর্যন্ত, স্ট্যান্ডার্ডটিতে মুভ কনস্ট্রাক্টর এবং মুভ অ্যাসাইনমেন্ট অপারেটরগুলির অন্তর্ভুক্ত জেনারেশন অন্তর্ভুক্ত ছিল যদিও সীমাবদ্ধতার মোটামুটি উল্লেখযোগ্য তালিকা সহ:

যদি দশম শ্রেণির সংজ্ঞা স্পষ্টভাবে কোনও মুভ কনস্ট্রাক্টরকে ঘোষণা না করে তবে একজনকে স্পষ্টতই খেলাপী হিসাবে ঘোষণা করা হবে এবং কেবল যদি
- এক্স এর ব্যবহারকারী-ঘোষিত অনুলিপি নির্মাণকারী নেই,
- এক্স-এর কোনও ব্যবহারকারী-ঘোষিত অনুলিপি অ্যাসাইনমেন্ট অপারেটর নেই ,
- এক্স-এর কোনও ব্যবহারকারী-ঘোষিত মুভ অ্যাসাইনমেন্ট অপারেটর নেই,
- এক্স-এর কোনও ব্যবহারকারী-ঘোষিত ডেস্ট্রাক্টর নেই এবং
- মুভ কনস্ট্রাক্টর মুছে ফেলা হিসাবে পরিষ্কারভাবে সংজ্ঞায়িত হবে না।

যদিও গল্পটি আছে তেমন কিছু নয়। একটি কর্টোর ঘোষিত হতে পারে, কিন্তু এখনও মুছে ফেলা হিসাবে সংজ্ঞায়িত:

একটি স্পষ্টভাবে ঘোষিত অনুলিপি / মুভ নির্মাণকারী তার শ্রেণীর একটি ইনলাইন পাবলিক সদস্য। দশম শ্রেণির জন্য একটি ডিফল্ট কপি / মুভ কনস্ট্রাক্টরকে মুছে ফেলা (8.4.3) হিসাবে সংজ্ঞায়িত করা হয় যদি এক্স থাকে:

- অ-তুচ্ছ সম্পর্কিত কনস্ট্রাক্টর এবং এক্স সহ একটি বৈকল্পিক সদস্য হ'ল ইউনিয়নের মতো শ্রেণি
- ক্লাস টাইপ এম (বা এর অ্যারে) এর একটি অ স্থিতিশীল ডেটা সদস্য যা অনুলিপি / সরানো যায় না কারণ ওভারলোড রেজোলিউশন (১৩.৩) হিসাবে এম এর সংশ্লিষ্ট কন্সট্রাক্টরের ক্ষেত্রে প্রয়োগ করা হয়েছে, দ্ব্যর্থহীন কন্সট্রাক্টর থেকে মুছে ফেলা বা অ্যাক্সেসযোগ্য কোনও ফাংশন বা ফলস্বরূপ ফলাফল ঘটে
- একটি সরাসরি বা ভার্চুয়াল বেস ক্লাস বি যা অনুলিপি / সরানো যায় না কারণ ওভারলোড রেজোলিউশন (13.3), বি এর সাথে সম্পর্কিত কনস্ট্রাক্টরের ক্ষেত্রে প্রয়োগ করা হয়েছে , অস্পষ্টতা বা ডিফল্ট কন্সট্রাক্টর থেকে মুছে ফেলা বা অ্যাক্সেসযোগ্য এমন কোনও ফাংশনের ফলস্বরূপ
- কোনও প্রত্যক্ষ বা ভার্চুয়াল বেস শ্রেণি বা ডিস্ট্রাক্টর এমন কোনও ধরণের অ-স্থিতিশীল ডেটা সদস্য যা ডিফল্ট কন্সট্রাক্টর থেকে মুছে ফেলা বা অ্যাক্সেসযোগ্য,
- অনুলিপি কনস্ট্রাক্টরের জন্য, মূলসূত্র রেফারেন্স প্রকারের একটি অ স্থিতিশীল ডেটা সদস্য, বা
- মুভ কনস্ট্রাক্টরের জন্য , স্থিতিবিহীন ডেটা সদস্য বা সরাসরি বা ভার্চুয়াল বেস শ্রেণীর সাথে এমন একটি ধরণের মুভ কনস্ট্রাক্টর নেই এবং তুচ্ছভাবে নয় অনুলিপিযোগ্য।


বর্তমান কার্যনির্বাহী খসড়াটি কিছু শর্তে অন্তর্নিহিত স্থানান্তর প্রজন্মকে মঞ্জুরি দেয় এবং আমি মনে করি যে রেজোলিউশনটি মূলত আব্রাহামের উদ্বেগকে সম্বোধন করে।
জেমস ম্যাকনেলিস

আমি নিশ্চিত না যে আমি বুঝতে পেরেছি যে টুইটগুলি 2 এবং টুইটের মধ্যে উদাহরণস্বরূপ কোন পদক্ষেপটি ভেঙে যেতে পারে 3। আপনি এটি ব্যাখ্যা করতে পারেন?
ম্যাথিউ এম।

@ ম্যাথিউ এম:: দুটি ত্বক এবং 3 টি ত্বক ভাঙা, এবং বেশ একইভাবে, সত্যিই। ট্যুইক 2-তে, এমন আক্রমণকারীদের সাথে ব্যক্তিগত সদস্যরা রয়েছেন যা মুভ কর্টারের দ্বারা ভেঙে যেতে পারে। ত্বিক ৩ এ, শ্রেণীর নিজস্ব ব্যক্তিগত সদস্য নেই , তবে যেহেতু এটি ব্যক্তিগত উত্তরাধিকার ব্যবহার করে , বেসের পাবলিক এবং সুরক্ষিত সদস্যরা প্রাপ্ত সমস্যাগুলির ব্যক্তিগত সদস্য হয়ে ওঠে, একই সমস্যার দিকে পরিচালিত করে।
জেরি কফিন

4
আমি কীভাবে মুভ কনস্ট্রাক্টর ক্লাস ইনগ্রান্টকে ভেঙে ফেলব তা সত্যি বুঝতে পারি নি 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;
};

এবং একটি মুভ-কনস্ট্রাক্টর এবং মুভ-এসাইনমেন্ট অপারেটর আসে।

(একদিকে যেমন, কেউ যদি জানতে পারে যে কীভাবে আমি বিশদগুলি এক ম্যাক্রোর সাথে সংযুক্ত করতে পারি তবে তা ফুলে উঠবে))


আপনাকে অনেক ধন্যবাদ, আমার মত অনুরূপ, বাদে আমাকে যুক্তি হিসাবে সদস্য ভেরিয়েবলের সংখ্যাটি পাস করতে হয়েছিল (যা সত্যই সফল হয়)।
ভিক্টর সেহর

4
@ ভিক্টর: সমস্যা নেই। যদি খুব বেশি দেরি না হয় তবে আমি মনে করি আপনার গ্রহণযোগ্য হিসাবে অন্য উত্তরগুলির মধ্যে একটি চিহ্নিত করা উচিত। আমার আরও ছিল "উপায় দ্বারা, এখানে একটি উপায়" এবং আপনার আসল প্রশ্নের উত্তর ছিল না।
GManNickG

4
যদি আমি আপনার ম্যাক্রোটি সঠিকভাবে পড়ছি, তবে আপনার সংকলক চূড়ান্তভাবে সরানো সদস্যদের প্রয়োগ করার সাথে সাথে, উপরের আপনার উদাহরণগুলি অনুলিপিযোগ্য হবে। স্পষ্টভাবে ঘোষিত পদক্ষেপের সদস্যরা উপস্থিত থাকলে কপির সদস্যদের অন্তর্নিহিত প্রজন্মকে বাধা দেওয়া হয়।
হাওয়ার্ড হিন্যান্ট

@ হাওয়ার্ড: এটি ঠিক আছে, এটি ততক্ষণ পর্যন্ত একটি অস্থায়ী সমাধান। :)
GManNickG

জিএমএন: এই ম্যাক্রোটি মুভকনস্ট্রাক্টর যুক্ত করেছে
ভিক্টর সেহর

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.