GCC9 স্ট্যান্ড :: ভেরিয়েন্টের মূল্যহীন অবস্থা এড়ানো কি অনুমোদিত?


14

আমি সম্প্রতি একটি রেডডিট আলোচনা অনুসরণ করেছি যা সংকলকগুলিতে std::visitঅপ্টিমাইজেশনের একটি দুর্দান্ত তুলনা করতে পারে । আমি নিম্নলিখিতটি লক্ষ্য করেছি: https://godbolt.org/z/D2Q5ED

সমস্ত ধরণের কিছু শর্ত পূরণ করার সময় GCC9 এবং Clang9 উভয়ই (আমি অনুমান করি যে তারা একই স্টাডলিব ভাগ করে নিচ্ছে) মূল্যহীন ব্যতিক্রম পরীক্ষা করার জন্য কোড তৈরি করে না। এটি উন্নত কোডজেনের দিকে পরিচালিত করে, তাই আমি এমএসভিসি এসটিএলে একটি সমস্যা উত্থাপন করেছি এবং এই কোড সহ উপস্থাপিত হয়েছিল:

template <class T>
struct valueless_hack {
  struct tag {};
  operator T() const { throw tag{}; }
};

template<class First, class... Rest>
void make_valueless(std::variant<First, Rest...>& v) {
  try { v.emplace<0>(valueless_hack<First>()); }
  catch(typename valueless_hack<First>::tag const&) {}
}

দাবি ছিল, যে এই কোন বৈকল্পিক মূল্যহীন করে তোলে, এবং পড়া ডকু এটি করা উচিত:

প্রথমে বর্তমানে বিদ্যমান মান (যদি থাকে তবে) নষ্ট করে দেয়। তারপরে অন্তর্ভুক্ত মানটিকে সরাসরি-সূচনা করে যেমন T_Iআর্গুমেন্টের সাথে টাইপের মান নির্মান করে std::forward<Args>(args)....যদি একটি ব্যতিক্রম ছুঁড়ে দেওয়া হয় তবে *thisমূল্যহীন_বি_অপত্তি হতে পারে।

যা আমি বুঝতে পারি না: কেন এটি "মে" হিসাবে বর্ণিত হয়েছে? পুরো অপারেশনটি ছুড়ে মারলে কি পুরানো অবস্থায় থাকা আইনসম্মত? কারণ এটিই জিসিসি করে:

  // For suitably-small, trivially copyable types we can create temporaries
  // on the stack and then memcpy them into place.
  template<typename _Tp>
    struct _Never_valueless_alt
    : __and_<bool_constant<sizeof(_Tp) <= 256>, is_trivially_copyable<_Tp>>
    { };

এবং পরে এটি (শর্তাধীন) এর মতো কিছু করে:

T tmp  = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);

সুতরাং মূলত এটি একটি অস্থায়ী তৈরি করে, এবং যদি এটি অনুলিপিগুলি / সফল স্থানে স্থানান্তরিত করে।

আইএমও এটি নথির বিবরণ অনুসারে "প্রথমে বিদ্যমান বিদ্যমান মানটি ধ্বংস করে" লঙ্ঘন। আমি যেমন স্ট্যান্ডার্ডটি পড়ি, তারপরে v.emplace(...)বৈকল্পের বর্তমান মানটি সর্বদা নষ্ট হয়ে যায় এবং নতুন প্রকারটি হয় সেট প্রকার বা মূল্যহীন।

আমি বুঝতে পারি যে শর্তটি is_trivially_copyableপর্যবেক্ষণযোগ্য ডেস্ট্রাক্টর রয়েছে এমন সমস্ত ধরণের বাদ দেয়। সুতরাং এটি যদিও হিসাবেও হতে পারে: "যেমন-যদি বৈকল্পিকটি পুরানো মানের সাথে পুনরায় পুনঃনির্মাণ করা হয়" বা তেমনি। তবে বৈকল্পিকের অবস্থা একটি পর্যবেক্ষণযোগ্য প্রভাব। তাহলে স্ট্যান্ডার্ডটি কি প্রকৃতপক্ষে অনুমতি দেয় emplaceযা বর্তমান মানটি পরিবর্তন করে না?

একটি স্ট্যান্ডার্ড উক্তির জবাবে সম্পাদনা করুন:

তারপরে থাকা মানটি এমনভাবে শুরু করে যেন আর্গুমেন্টের সাহায্যে টিআই টাইপের মান প্রত্যক্ষ-অ-তালিকা-সূচনা করে std​::​forward<Args>(args)...

কি T tmp {std​::​forward<Args>(args)...}; this->value = std::move(tmp);সত্যিই উপরোক্ত একটি বৈধ বাস্তবায়ন হিসাবে গণনা? এটাই কি "যেন" বলতে বোঝায়?

উত্তর:


7

আমি মনে করি মানটির গুরুত্বপূর্ণ অংশটি হ'ল:

Https://mittedong-cpp.github.io/cppwp/n4659/variant.mod#12 থেকে

23.7.3.4 মোদী rs এরস

(...)

টেমপ্লেট বৈকল্পিক_আপনিটিভাল_টি> & এম্পলেস (আর্টস && ... আরগস);

(...) যদি অন্তর্ভুক্ত মানটির প্রারম্ভিককরণের সময় কোনও ব্যতিক্রম ছুঁড়ে দেওয়া হয় তবে বৈকল্পিকটি কোনও মান ধরে রাখতে পারে না

এটি "সম্ভবত" না "অবশ্যই" বলেছে says আমি সিসিএসের মতো ব্যবহারের মতো প্রয়োগের অনুমতি দেওয়ার জন্য এটি ইচ্ছাকৃত হওয়ার আশা করব।

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

ফলোআপ প্রশ্ন:

Then initializes the contained value as if direct-non-list-initializing a value of type TI with the arguments std​::​forward<Args>(args)....

T tmp {std :: forward (args) ...}; this-> মান = std :: চাল (tmp); সত্যিই উপরের একটি বৈধ বাস্তবায়ন হিসাবে গণনা? এটাই কি "যেন" বলতে বোঝায়?

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


মজাদার. আমি ফলো-আপ / স্পষ্টকরণের অনুরোধের সাথে প্রশ্নটি আপডেট করেছি। মূলটি হ'ল: অনুলিপি / সরানো অনুমোদিত? আমি might/mayশব্দটি দ্বারা খুব বিভ্রান্ত হয়েছি কারণ মানটি বিকল্পটি কী তা বলে না।
শিখা ফায়ারফায়ার

মানক উক্তিটির জন্য এটি গ্রহণ করা এবং there is no way to detect the difference
শিখুন

5

সুতরাং স্ট্যান্ডার্ড সত্যিই অনুমতি দেয় যে emplace যা বর্তমান মানটি পরিবর্তন করে না?

হ্যাঁ. emplaceকোনও ফাঁস না হওয়ার (যেমন, নির্মাণ ও ধ্বংসগুলি পর্যবেক্ষণযোগ্য পার্শ্ব প্রতিক্রিয়া সৃষ্টি করার সময় অবজেক্টের আজীবন সম্মানের) বুনিয়াদি গ্যারান্টি সরবরাহ করবে তবে যখন সম্ভব হয় তখন দৃ the় গ্যারান্টি সরবরাহ করার অনুমতি দেওয়া হয় (অর্থাত্ কোনও অপারেশন ব্যর্থ হলে মূল অবস্থাটি রাখা হয়)।

variantএকটি ইউনিয়নের অনুরূপ আচরণ করা প্রয়োজন - বিকল্পগুলি উপযুক্ত বরাদ্দকৃত স্টোরেজের একটি অঞ্চলে বরাদ্দ করা হয়। এটি গতিশীল মেমরি বরাদ্দ করার অনুমতি নেই। অতএব, একটি টাইপ-চেঞ্জিংemplace অতিরিক্ত পদক্ষেপের কনস্ট্রাক্টরকে কল না করে আসল অবজেক্টটি রাখার কোনও উপায় নেই - এটি এটি ধ্বংস করে তার জায়গায় নতুন বস্তুটি তৈরি করতে হবে। যদি এই নির্মাণ ব্যর্থ হয়, তবে রূপটি ব্যতিক্রমী মূল্যহীন অবস্থায় যেতে হবে। এটি কোনও অস্তিত্বহীন বস্তু ধ্বংস করার মতো অদ্ভুত জিনিসগুলি প্রতিরোধ করে।

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

একটি স্ট্যান্ডার্ড উক্তির জবাবে সম্পাদনা করুন:

তারপরে থাকা মানটি এমনভাবে শুরু করে যেন আর্গুমেন্টের সাহায্যে টিআই টাইপের মান প্রত্যক্ষ-অ-তালিকা-সূচনা করে std​::​forward<Args>(args)...

নেই T tmp {std​::​forward<Args>(args)...}; this->value = std::move(tmp); সত্যিই উপরোক্ত একটি বৈধ বাস্তবায়ন হিসাবে গণনা? এটাই কি "যেন" বলতে বোঝায়?

হ্যাঁ, যদি সরানো অ্যাসাইনমেন্টটি কোনও পর্যবেক্ষণযোগ্য প্রভাব তৈরি করে না, যা তুচ্ছভাবে অনুলিপিযোগ্য প্রকারের ক্ষেত্রে।


আমি যুক্তি যুক্তির সাথে পুরোপুরি একমত। আমি নিশ্চিত না যে এটি আসলে স্ট্যান্ডার্ডে আছে? আপনি কিছু দিয়ে এই ব্যাক আপ করতে পারেন?
শিখা ফায়ারফায়ার

@ ফ্লেমফায়ার হুম ... সাধারণভাবে, মানক কার্যকারিতাগুলি মৌলিক গ্যারান্টি সরবরাহ করে (যদি ব্যবহারকারী ব্যবহার করে যা কিছু থাকে তাতে ভুল না থাকে), এবং std::variantএটি ভঙ্গ করার কোনও কারণ নেই। আমি সম্মত হই যে এটি স্ট্যান্ডার্ডের শব্দের ক্ষেত্রে আরও স্পষ্ট করা যায়, তবে এটি মূলত অন্যরা স্ট্যান্ডার্ড লাইব্রেরির অংশ হিসাবে কাজ করে। এবং FYI, P0088 প্রাথমিক প্রস্তাব ছিল।
এলএফ

ধন্যবাদ। ভিতরে আরও সুস্পষ্ট স্পেসিফিকেশন রয়েছে: if an exception is thrown during the call toT’s constructor, valid()will be false;সুতরাং এটি এই "অপ্টিমাইজেশান" নিষিদ্ধ করেছিল
ফ্ল্যামফায়ার

হ্যাঁ. emplaceP0088 এর অধীনে স্পেসিফিকেশনException safety
শিখুন

@ ফ্লেমেফায়ার মনে হয় মূল প্রস্তাব এবং সংস্করণটি যে ভোট দিয়েছে তাতে পার্থক্য রয়েছে The চূড়ান্ত সংস্করণটি "মে" শব্দটিতে পরিবর্তিত হয়েছে।
এলএফ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.