কেন আমরা `std :: মুভ a একটি` কনস্ট `বস্তুর উপর ব্যবহার করতে পারি?


113

সি ++ এ, আমরা এই কোডটি লিখতে পারি:

struct Cat {
   Cat(){}
};

const Cat cat;
std::move(cat); //this is valid in C++11

আমি যখন ফোন করি তখন এর std::moveঅর্থ আমি অবজেক্টটি সরিয়ে নিতে চাই, অর্থাৎ আমি বস্তুটি পরিবর্তন করব। কোনও constবস্তুর সরানো অযৌক্তিক, সুতরাং কেন std::moveএই আচরণটি সীমাবদ্ধ করে না? এটি ভবিষ্যতে একটি ফাঁদ হবে, তাই না?

ব্র্যান্ডন মন্তব্যে উল্লিখিত হিসাবে এখানে ফাঁদ বোঝানো হয়েছে:

"আমি মনে করি তার অর্থ এই" ফাঁদ "তাকে চুপচাপ লুকিয়ে আছে কারণ যদি সে বুঝতে না পারে, তবে তার একটি অনুলিপি শেষ হয়েছে যা তার উদ্দেশ্য নয়।"

স্কট মায়ার্সের "কার্যকর আধুনিক সি ++" বইয়ে তিনি একটি উদাহরণ দিয়েছেন:

class Annotation {
public:
    explicit Annotation(const std::string text)
     : value(std::move(text)) //here we want to call string(string&&),
                              //but because text is const, 
                              //the return type of std::move(text) is const std::string&&
                              //so we actually called string(const string&)
                              //it is a bug which is very hard to find out
private:
    std::string value;
};

যদি std::moveকোনও constজিনিসে অপারেট করতে নিষেধ করা হয় , আমরা সহজেই বাগটি খুঁজে পেতে পারি, তাই না?


2
তবে এটি সরানোর চেষ্টা করুন। এর রাষ্ট্র পরিবর্তন করার চেষ্টা করুন। std::moveনিজে থেকে বস্তুর কিছুই করে না। এক তর্ক করতে পারে std::moveখারাপ নাম দেওয়া হয়।
juanchopanza

3
এটি আসলে কিছুই সরায় না । এটি যা কিছু করে তা কোনও মূল্যের রেফারেন্সে ফেলে দেওয়া হয়। চেষ্টা করুন CAT cat2 = std::move(cat);, ধরে CATনিচ্ছেন নিয়মিত পদক্ষেপ-অ্যাসাইনমেন্ট সমর্থন করে।
WhozCraig

11
std::moveকেবলমাত্র castালাই, এটি আসলে কিছুই সরায় না
রেড সতর্কতা

2
@ হোউজক্রাইগ: সাবধান, আপনি পোস্ট করেছেন এমন কোড হিসাবে সংকেত সংকলন করে এবং কার্যকর করে, যাতে এটিকে বিভ্রান্তিকর করে তোলে।
মাকিং হাঁস

1
@ মুভিংডাক কখনও বলেনি যে এটি সংকলন করবে না। এটি কেবলমাত্র ডিফল্ট অনুলিপি-কর্টর সক্ষম করার কারণে কাজ করে। স্কোয়ালেচ এবং চাকাগুলি পড়ে যায়।
WhozCraig

উত্তর:


55
struct strange {
  mutable size_t count = 0;
  strange( strange const&& o ):count(o.count) { o.count = 0; }
};

const strange s;
strange s2 = std::move(s);

এখানে আমরা একটি ব্যবহার দেখতে std::moveএকটি উপর T const। এটি a T const&&। এটির জন্য আমাদের কাছে একটি মুভ কনস্ট্রাক্টর রয়েছে strangeঠিক এই ধরণের।

এবং এটি বলা হয়।

এখন, এটি সত্য যে আপনার প্রস্তাব ঠিক করা বাগগুলির চেয়ে এই অদ্ভুত প্রকারটি বিরল।

তবে, অন্যদিকে, std::moveজেনেরিক কোডে বিদ্যমান বিদ্যমানগুলি আরও ভালভাবে কাজ করে, যেখানে আপনি জানেন না যে আপনি যে ধরণের সাথে কাজ করছেন তা একটি Tবা একটি T const


3
প্রথম উত্তর হওয়ার জন্য +1 যা আসলে আপনি কেন কোনও বস্তুর উপর কল করতে চান তা বোঝানোর চেষ্টা std::moveকরে const
ক্রিস ড্রু

একটি ফাংশন গ্রহণ দেখাচ্ছে জন্য +1 const T&&। এটি "আমি একটি মূল্যায়ন-রেফ নেব তবে আমি প্রতিশ্রুতি দিচ্ছি যে আমি এটি সংশোধন করব না" ধরণের একটি "এপিআই প্রোটোকল" প্রকাশ করে। আমার ধারণা, পরিবর্তনীয় ব্যবহার করার সময় এটি অস্বাভাবিক apart হয়তো অন্য কোনও ব্যবহারের ক্ষেত্রে forward_as_tupleপ্রায় কোনও কিছু ব্যবহার করতে সক্ষম হতে হবে এবং পরে এটি ব্যবহার করতে পারি।
এফ পেরেরা

107

এখানে এমন একটি কৌশল আছে যা আপনি উপেক্ষা করছেন, এটি std::move(cat) আসলে কিছুই সরায় না । এটি কেবল সংকলককে সরানোর চেষ্টা করতে বলে। তবে, যেহেতু আপনার শ্রেণীর কোনও কনস্ট্রাক্টর নেই যে এটি গ্রহণ করে const CAT&&, এটি পরিবর্তে অনিষ্টকৃত const CAT&অনুলিপি নির্মাণকারী এবং নিরাপদে অনুলিপি ব্যবহার করবে । কোন বিপদ, কোন ফাঁদ। যদি অনুলিপি নির্মাণকারীর কোনও কারণে অক্ষম করা থাকে তবে আপনি একটি সংকলক ত্রুটি পাবেন।

struct CAT
{
   CAT(){}
   CAT(const CAT&) {std::cout << "COPY";}
   CAT(CAT&&) {std::cout << "MOVE";}
};

int main() {
    const CAT cat;
    CAT cat2 = std::move(cat);
}

প্রিন্ট COPY, না MOVE

http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f

মনে রাখবেন যে কোড আপনি উল্লেখ করেছেন তাতে বাগটি একটি পারফরম্যান্স ইস্যু, কোনও স্থিতিশীলতার সমস্যা নয়, সুতরাং এই জাতীয় বাগটি কখনও ক্রাশের কারণ হবে না। এটি কেবল একটি ধীর অনুলিপি ব্যবহার করবে। তদুপরি, এই জাতীয় বাগটি নন-কনস্ট্যান্ট অবজেক্টগুলির জন্যও ঘটে যাগুলির মুভ কনস্ট্রাক্টর নেই, সুতরাং কেবল একটি constওভারলোড যুক্ত করলে সেগুলি সবই ধরা পড়বে না। আমরা প্যারামিটার ধরণের থেকে নির্ধারিত স্থানান্তর বা সরানো নিয়োগের সক্ষমতা পরীক্ষা করতে পারতাম, তবে এটি জেনেরিক টেম্পলেট কোডের সাথে হস্তক্ষেপ করবে যা অনুলিপি নির্মাণকারীর পিছনে ফিরে আসবে বলে মনে করা হচ্ছে । এবং হেক, হতে পারে যে কেউ const CAT&&কার কাছ থেকে নির্মাণ করতে সক্ষম হতে চান , আমি কে বলতে পারি যে সে পারছে না?


Upticked। নিয়মিত মুভ-কনস্ট্রাক্টর বা অ্যাসাইনমেন্ট অপারেটরের ব্যবহারকারী-সংজ্ঞায়িত সংজ্ঞা অনুসারে অনুলিপি মুছে ফেলার বিষয়টি বিবেচনা করেও এটি ভাঙ্গা সংকলনের মাধ্যমে প্রদর্শিত হবে। চমৎকার উত্তর.
WhozCraig

আরও উল্লেখ করার মতো যে একটি অনুলিপি তৈরির অনুলিপিটিরও constকোনও সহায়তা করবে না। [শ্রেণি.কপি] §8: "অন্যথায়, সুস্পষ্টভাবে ঘোষিত অনুলিপি নির্মাণকারীর ফর্মটি থাকবে X::X(X&)"
উত্সর্গকারী

9
আমি মনে করি না তিনি কম্পিউটার / সমাবেশের ক্ষেত্রে "ফাঁদ" বোঝালেন। আমি মনে করি তার অর্থ এটি "ফাঁদ" তাকে চুপচাপ লুকিয়ে আছে কারণ যদি সে বুঝতে না পারে, তবে সে একটি অনুলিপিটি শেষ করবে যা তার উদ্দেশ্য নয়। আমার ধারণা ..
ব্র্যান্ডন

সোনার ব্যাজটির জন্য আপনি আরও 1 টি আপভোট পেতে এবং আমি আরও 4 জন পেতে পারি। ;)
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

যে কোড নমুনায় উচ্চ পাঁচ। সত্যিই ভাল পয়েন্ট পয়েন্ট পেতে।
ব্রেন্ট লিখেছেন কোড

20

বাকি উত্তরগুলি এখনও অবহেলা করার একটি কারণ হ'ল জেনেরিক কোডটি সরিয়ে যাওয়ার ক্ষেত্রে স্থিতিস্থাপক হওয়ার ক্ষমতা । উদাহরণস্বরূপ বলা যাক যে আমি একটি জেনেরিক ফাংশন লিখতে চেয়েছিলাম যা এক ধরণের কনটেইনার থেকে সমস্ত উপাদানকে একই মান সহ অন্য ধরণের ধারক তৈরি করতে সরিয়ে নিয়েছিল:

template <class C1, class C2>
C1
move_each(C2&& c2)
{
    return C1(std::make_move_iterator(c2.begin()),
              std::make_move_iterator(c2.end()));
}

শীতল, এখন আমি তুলনামূলকভাবে দক্ষতার একটি vector<string>থেকে একটি তৈরি করতে পারি deque<string>এবং প্রতিটি ব্যক্তি stringপ্রক্রিয়াটিতে স্থানান্তরিত হবে।

তবে আমি যদি একটি থেকে সরানো চাই map?

int
main()
{
    std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
    auto v = move_each<std::vector<std::pair<int, std::string>>>(m);
    for (auto const& p : v)
        std::cout << "{" << p.first << ", " << p.second << "} ";
    std::cout << '\n';
}

যদি std::moveকোনও অ- constতর্ক করার জন্য জোর দেওয়া হয় , তবে উপরের ইনস্ট্যান্টেশনটি move_eachসংকলন করবে না কারণ এটি একটি const int( key_typeএর map) স্থানান্তরিত করার চেষ্টা করছে । কিন্তু এই কোড গ্রাহ্য না করে যদি এটি সরাতে পারবেন না key_type। পারফরম্যান্সের কারণে এটি mapped_type( std::string) সরাতে চায় ।

এটা এই উদাহরণস্বরূপ, এবং জেনেরিক কোডিং যে এটা পছন্দ অগণিত অন্যান্য উদাহরণ std::moveএকটি হল সরাতে অনুরোধ সরাতে চাহিদার না।


2

ওপি'র মতোই আমারও উদ্বেগ রয়েছে।

std :: সরানো কোনও বস্তুকে সরিয়ে দেয় না, বস্তুটির চলনযোগ্যতাও নিশ্চিত করে না। তাহলে এটিকে চাল বলা হয় কেন?

আমার মনে হয় অস্থাবর না হওয়া নিম্নলিখিত দুটি পরিস্থিতিতে একটি হতে পারে:

1. চলন্ত টাইপ কনস্ট।

আমাদের ভাষায় কনস্ট কীওয়ার্ড থাকার কারণটি হ'ল আমরা কম্পাইলারটি কনস্টের দ্বারা সংজ্ঞায়িত কোনও বস্তুর কোনও পরিবর্তন রোধ করতে চাই। স্কট মেয়ার্স বইয়ের উদাহরণ দেওয়া:

    class Annotation {
    public:
     explicit Annotation(const std::string text)
     : value(std::move(text)) // "move" text into value; this code
     {  } // doesn't do what it seems to!    
     
    private:
     std::string value;
    };

এর আক্ষরিক অর্থ কী? মান সদস্যের কাছে একটি কনস্ট্রিং স্ট্রিং সরান - কমপক্ষে, আমি ব্যাখ্যাটি পড়ার আগে এটি আমার বোধগম্য।

ভাষাটি যদি নড়াচড়া না করার বা গ্যারান্টি না দেওয়ার জন্য স্ট্যান্ড :: :: মুভ () বলা হয় তখন প্রযোজ্য হয়, তবে শব্দ মুভ ব্যবহার করার সময় এটি আক্ষরিকভাবে বিভ্রান্তিকর হয়।

ভাষা যদি স্ট্যান্ড :: ব্যবহারের মাধ্যমে আরও দক্ষতার দক্ষতা অর্জনের জন্য মানুষকে উত্সাহিত করে, বিশেষত এই ধরণের স্পষ্ট আক্ষরিক দ্বন্দ্বের জন্য এটি যত তাড়াতাড়ি সম্ভব এই জাতীয় ফাঁদগুলি রোধ করতে হবে।

আমি সম্মত হই যে লোকেরা সচেতন হওয়া উচিত স্থির স্থানান্তর করা অসম্ভব, তবে এই বাধ্যবাধকতাটি বোঝানো উচিত নয় যে স্পষ্ট বিপরীতে ঘটলে সংকলক নীরব থাকতে পারে।

২. অবজেক্টটির কোনও মুভ কনস্ট্রাক্টর নেই

ব্যক্তিগতভাবে, আমি মনে করি এটি ওপির উদ্বেগের থেকে আলাদা গল্প, যেমন ক্রিস ড্রিউ বলেছিলেন

@hvd এটি আমার কাছে কিছুটা অ-তর্ক হিসাবে মনে হচ্ছে। ওপির পরামর্শটি বিশ্বের সমস্ত বাগ সংশোধন করে না বলে অগত্যা এটি খারাপ ধারণা বলে বোঝায় না (এটি সম্ভবত আপনিই দিচ্ছেন তবে এটি নয়)। - ক্রিস ড্র


1

আমি অবাক হই কেউ এর পিছনে সামঞ্জস্যতার দিকটি উল্লেখ করেনি। আমি বিশ্বাস করি, std::moveসি ++ 11 এ উদ্দেশ্যমূলকভাবে এটি তৈরি করা হয়েছিল। কল্পনা করুন যে আপনি কোনও লিগ্যাসি কোডবেস নিয়ে কাজ করছেন, যেটি সি ++ 98 লাইব্রেরিতে খুব বেশি নির্ভর করে, তাই অনুলিপি অ্যাসাইনমেন্টের ফলব্যাক ব্যতীত চলন্ত জিনিসগুলি ভেঙে ফেলবে।


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