আমি কীভাবে অনুরূপ কনস্ট্যান্ড এবং নন-কনস্ট্যান্ড সদস্য ফাংশনগুলির মধ্যে কোড সদৃশতা সরিয়ে ফেলব?


242

ধরা যাক আমার নিম্নলিখিত আছে class X যেখানে অভ্যন্তরীণ সদস্যের অ্যাক্সেস ফিরিয়ে দিতে চাই সেখানে :

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    Z& Z(size_t index)
    {
        // massive amounts of code for validating index

        Z& ret = vecZ[index];

        // even more code for determining that the Z instance
        // at index is *exactly* the right sort of Z (a process
        // which involves calculating leap years in which
        // religious holidays fall on Tuesdays for
        // the next thousand years or so)

        return ret;
    }
    const Z& Z(size_t index) const
    {
        // identical to non-const X::Z(), except printed in
        // a lighter shade of gray since
        // we're running low on toner by this point
    }
};

এই দুই সদস্যের কাজ করা হয় X::Z()এবং X::Z() constধনুর্বন্ধনী ভিতরে অভিন্ন কোড আছে। এটি সদৃশ কোড এবং জটিল যুক্তিযুক্ত দীর্ঘ ফাংশনগুলির জন্য রক্ষণাবেক্ষণের সমস্যা তৈরি করতে পারে

এই কোড সদৃশ এড়ানো উপায় আছে?


এই উদাহরণে আমি কনস্টের ক্ষেত্রে একটি মান ফিরিয়ে দেব যাতে আপনি নীচে রিফ্যাক্টরিং করতে পারবেন না। int Z () কনস্ট const রিটার্ন z; }
ম্যাট দাম

1
মৌলিক ধরণের জন্য, আপনি একেবারে সঠিক! আমার প্রথম উদাহরণ খুব ভাল ছিল না। এর পরিবর্তে আমরা কিছু বর্গ উদাহরণ ফিরে যাচ্ছি যে বলুন। (আমি এটি প্রতিফলিত করার জন্য প্রশ্ন আপডেট করেছি))
কেভিন

উত্তর:


189

বিস্তারিত ব্যাখ্যার জন্য, দয়া করে পি এর উপর " সদৃশ constএবং সদস্যবিহীন কার্য এড়ানো" শিরোনামটি দেখুন const। 23, আইটেম 3 এ " স্কটি মিয়েরস দ্বারা কার্যকর সি ++ , 3 ডি এডিটে const" যখনই সম্ভব ব্যবহার করুন "ব্যবহার করুন , আইএসবিএন -13: 9780321334879।

বিকল্প পাঠ

এখানে মায়ারদের সমাধান (সরলীকৃত):

struct C {
  const char & get() const {
    return c;
  }
  char & get() {
    return const_cast<char &>(static_cast<const C &>(*this).get());
  }
  char c;
};

দুটি কাস্ট এবং ফাংশন কল কুশ্রী হতে পারে তবে এটি সঠিক। মায়ারদের এর পুরো ব্যাখ্যা রয়েছে।


45
স্কট মায়ার্স অনুসরণ করার জন্য কেউ কখনও বরখাস্ত হয়নি :-)
স্টিভ জেসোপ

11
উইটক্যাম্প সঠিক যে সাধারণভাবে কনস্ট_কাস্ট ব্যবহার করা খারাপ। এটি একটি নির্দিষ্ট ক্ষেত্রে যেখানে এটি নয়, যেমনটি মায়ার্স ব্যাখ্যা করেছেন। @ অ্যাডাম: রম => কনস্ট্যান্ট ঠিক আছে। কনস্ট == রম স্পষ্টতই বোকামি যেহেতু যে কেউ নন-কনস্টকে উইলি-নিলিতে কাস্ট করতে পারে: এটি কোনও কিছুর সংশোধন না করা পছন্দ করার সমতুল্য।
স্টিভ জেসোপ

44
সাধারণভাবে আমি স্থির_কাস্টের পরিবর্তে কনস্ট_কাস্ট ব্যবহার করার পরামর্শ দিচ্ছি কারণ এটি দুর্ঘটনাক্রমে টাইপটি পরিবর্তন করা থেকে বিরত করে preven
গ্রেগ রজার্স

6
@HelloGoodbye: আমি মায়ার্স একটি অনুমান মনে সামান্য পরিমাণ বর্গ ইন্টারফেসের ডিজাইনার থেকে বুদ্ধি। যদি get()constকনট কনজেক্ট হিসাবে সংজ্ঞায়িত এমন কোনও কিছু প্রদান করে, তবে এর কোনও অ-কনস্ট্যান্ট সংস্করণ থাকা উচিত নয় get()। প্রকৃতপক্ষে এ সম্পর্কে আমার চিন্তাভাবনা সময়ের সাথে সাথে পরিবর্তিত হয়েছে: টেম্পলেট সমাধানটি হ'ল অনুলিপি এড়ানোর এবং সংকলক-পরীক্ষা করা কনস্ট-যথার্থতা পাওয়ার একমাত্র উপায় , তাই ব্যক্তিগতভাবে আমি const_castনকল কোড এড়ানোর জন্য আর কোনও ব্যবহার করব না, আমি নির্বাণের মধ্যে বেছে নেব কোনও ফাংশন টেম্পলেটে বিভক্ত কোডটি অন্যথায় এটিকে ফাঁকি দেওয়া।
স্টিভ জেসোপ

7
নীচের দুটি টেমপ্লেটগুলি এই সমাধানটির পাঠযোগ্যতার সাথে প্রচুর পরিমাণে সহায়তা করে: template<typename T> const T& constant(T& _) { return const_cast<const T&>(_); }এবং template<typename T> T& variable(const T& _) { return const_cast<T&>(_); }। তারপরে আপনি এটি করতে পারেন:return variable(constant(*this).get());
কেসি রডর্মার

64

হ্যাঁ, কোডের সদৃশতা এড়ানো সম্ভব। যুক্তি রাখতে আপনার কনস্টের সদস্য ফাংশনটি ব্যবহার করতে হবে এবং নন-কনস্ট্যান্ট সদস্য ফাংশনটি কনস্ট মেম্বার ফাংশনটি কল করতে হবে এবং রি-রিটার্নের মানটি নন-কনস্ট্যান্ট রেফারেন্সে পুনরায় কাস্ট করতে হবে (বা ফাংশনটি যদি পয়েন্টার দেয় তবে পয়েন্টার):

class X
{
   std::vector<Z> vecZ;

public:
   const Z& z(size_t index) const
   {
      // same really-really-really long access 
      // and checking code as in OP
      // ...
      return vecZ[index];
   }

   Z& z(size_t index)
   {
      // One line. One ugly, ugly line - but just one line!
      return const_cast<Z&>( static_cast<const X&>(*this).z(index) );
   }

 #if 0 // A slightly less-ugly version
   Z& Z(size_t index)
   {
      // Two lines -- one cast. This is slightly less ugly but takes an extra line.
      const X& constMe = *this;
      return const_cast<Z&>( constMe.z(index) );
   }
 #endif
};

উল্লেখ্য: এটি যে আপনি কি গুরুত্বপূর্ণ নয় অ const ফাংশনে যুক্তিবিজ্ঞান করা const-ফাংশন কল অ const ফাংশন আছে - এটা অনির্ধারিত আচরণের পরিণাম হতে পারে। কারণটি হ'ল ধ্রুবক শ্রেণীর উদাহরণটি অ-ধ্রুবক উদাহরণ হিসাবে কাস্ট হয়। নন-কনস্ট্যান্ড সদস্য ফাংশনটি দুর্ঘটনাক্রমে শ্রেণিটি সংশোধন করতে পারে, যা সি ++ স্ট্যান্ডার্ড রাজ্যগুলির অপরিবর্তিত আচরণের ফলে দেখা দেয়।


3
বাহ ... ভয়াবহ। আপনি কেবল কোডের পরিমাণ বাড়িয়েছেন, স্বচ্ছতা হ্রাস করেছেন এবং দুটি দুর্গন্ধযুক্ত কনস্ট_কাস্ট <> গুলি যুক্ত করেছেন। আপনার মনে সম্ভবত একটি উদাহরণ রয়েছে যেখানে এটি আসলে বোঝায়?
শোগ 9

14
আরে এটি ডিঙ করবেন না, এটি কুরুচিপূর্ণ হতে পারে, তবে স্কট মেয়ার্সের মতে এটি (প্রায়) সঠিক উপায়। দেখুন কার্যকরী সি ++ , 3D ইডি, শিরোনাম "const এবং অ খরচ সদস্য ফাংশন মধ্যে অনুলিপি এড়ানো নীচে আইটেমটি 3।
jwfearn

17
আমি বুঝতে পারি যে সমাধানটি কুশ্রী হতে পারে, তবে কল্পনা করুন যে কোডটি কী ফিরিয়ে আনবে তা নির্ধারণ করে 50 লম্বা লম্বা। তারপরে অনুলিপি অত্যন্ত অনাকাঙ্ক্ষিত - বিশেষত যখন আপনাকে কোডটি পুনরায় ফ্যাক্টর করতে হয়। আমি আমার ক্যারিয়ারে অনেকবার এর মুখোমুখি হয়েছি।
কেভিন

8
এটির এবং মেয়ারগুলির মধ্যে পার্থক্য হ'ল মায়ার্সের স্ট্যাটিক_কাস্ট <কনস্ট এক্স &> (* এটি) রয়েছে। কনস্ট_কাস্ট কনস্টেন্ট অপসারণের জন্য, এটি যুক্ত না করার জন্য।
স্টিভ জেসোপ

8
@ ভায়োলেটজিরাফ আমরা জানি যে অবজেক্টটি মূলত কনস্টেট তৈরি করা হয়নি, কারণ এটি কোনও নন কনস্ট্যান্ট অবজেক্টের একটি নন-কনস্ট্যান্ট সদস্য, যা আমরা জানি কারণ আমরা বলেছি যে অবজেক্টটির একটি নন-কনস্ট্যান্ড পদ্ধতিতে রয়েছি। সংকলকটি এই অনুভূতিটি তৈরি করে না, এটি একটি রক্ষণশীল নিয়ম অনুসরণ করে। আপনি কেন মনে করেন কনস্ট_কাস্ট এই ধরণের পরিস্থিতির জন্য না থাকলে?
কালেথ

47

সি ++ 17 এই প্রশ্নের সেরা উত্তর আপডেট করেছে:

T const & f() const {
    return something_complicated();
}
T & f() {
    return const_cast<T &>(std::as_const(*this).f());
}

এর এটির সুবিধাগুলি রয়েছে:

  • যা চলছে তা সুস্পষ্ট
  • ন্যূনতম কোড ওভারহেড রয়েছে - এটি একটি একক লাইনে ফিট করে
  • ভুল পাওয়া শক্ত (কেবলমাত্র volatileদুর্ঘটনায় দূরে সরে যেতে পারে তবে volatileএটি একটি বিরল যোগ্য)

আপনি যদি পুরো ছাড়ের পথে যেতে চান তবে এটি কোনও সহায়ক ফাংশন করে সম্পন্ন করা যেতে পারে

template<typename T>
constexpr T & as_mutable(T const & value) noexcept {
    return const_cast<T &>(value);
}
template<typename T>
constexpr T * as_mutable(T const * value) noexcept {
    return const_cast<T *>(value);
}
template<typename T>
constexpr T * as_mutable(T * value) noexcept {
    return value;
}
template<typename T>
void as_mutable(T const &&) = delete;

এখন আপনি এমনকি জগাখিচুড়ি করতে পারবেন না volatile, এবং ব্যবহারের মতো দেখাচ্ছে

decltype(auto) f() const {
    return something_complicated();
}
decltype(auto) f() {
    return as_mutable(std::as_const(*this).f());
}

লক্ষ্য করুন সঙ্গে "as_mutable" const rvalue জমিদার মুছে যাওয়া (যা সাধারণত বাঞ্ছনীয়) যদি কাজ করা থেকে শেষ উদাহরণ বাধা দেয় f()আয় Tপরিবর্তে T&
ম্যাক্স ট্রুকসা

1
@ ম্যাক্সট্রিক্সা: হ্যাঁ, এবং এটি একটি ভাল জিনিস। যদি এটি কেবল সংকলিত হয় তবে আমাদের কাছে ঝুঁকির রেফারেন্স থাকবে। যে ক্ষেত্রে f()রিটার্ন আসে T, আমরা দুটি ওভারলোড নিতে চাই না, কেবল constসংস্করণই যথেষ্ট।
ডেভিড স্টোন

খুব সত্য, আমি গতকাল আমার সম্পূর্ণ অনাবৃত মস্তিষ্কের জন্য ক্ষমা চাইছি, আমি মন্তব্যটি লেখার সময় আমি কী ভাবছিলাম তা সম্পর্কে ধারণা নেই। আমি একটি ফিরতি একটি const / চপল সংগ্রহকারী যুগল দিকে তাকিয়ে ছিল shared_ptr। সুতরাং আমার আসলে as_mutable_ptrযা কিছু প্রয়োজন তা ছিল যা as_mutableউপরের প্রায় সাদৃশ্য দেখায় , এটি গ্রহণ করে এবং পরিবর্তে shared_ptrব্যবহার std::const_pointer_castকরে এবং ব্যবহার করে const_cast
ম্যাক্স ট্রুকসা

1
যদি কোনও পদ্ধতি ফিরে আসে T const*তবে এটি T const* const&&বাঁধাইয়ের পরিবর্তে আবদ্ধ হবে T const* const&(কমপক্ষে আমার পরীক্ষায় এটি হয়েছে)। T const*পয়েন্টার ফিরিয়ে দেওয়ার পদ্ধতির আর্গুমেন্ট টাইপ হিসাবে আমাকে একটি ওভারলোড যুক্ত করতে হয়েছিল ।
monkey0506

2
@ monkey0506: আমি সমর্থনকারী পয়েন্টারগুলিকে পাশাপাশি রেফারেন্সগুলিতে আমার উত্তর আপডেট করেছি
ডেভিড স্টোন

34

আমি মনে করি একটি পরীক্ষামূলক সহায়তা ফাংশন ব্যবহার করে স্কট মেয়ার্সের সমাধান সি ++ 11 এ উন্নত করা যেতে পারে। এটি অভিপ্রায়টিকে আরও সুস্পষ্ট করে তোলে এবং অন্যান্য অনেক প্রাপ্তদের জন্য এটি পুনরায় ব্যবহার করা যেতে পারে।

template <typename T>
struct NonConst {typedef T type;};
template <typename T>
struct NonConst<T const> {typedef T type;}; //by value
template <typename T>
struct NonConst<T const&> {typedef T& type;}; //by reference
template <typename T>
struct NonConst<T const*> {typedef T* type;}; //by pointer
template <typename T>
struct NonConst<T const&&> {typedef T&& type;}; //by rvalue-reference

template<typename TConstReturn, class TObj, typename... TArgs>
typename NonConst<TConstReturn>::type likeConstVersion(
   TObj const* obj,
   TConstReturn (TObj::* memFun)(TArgs...) const,
   TArgs&&... args) {
      return const_cast<typename NonConst<TConstReturn>::type>(
         (obj->*memFun)(std::forward<TArgs>(args)...));
}

এই সহায়ক ফাংশনটি নিম্নলিখিত উপায়ে ব্যবহার করা যেতে পারে।

struct T {
   int arr[100];

   int const& getElement(size_t i) const{
      return arr[i];
   }

   int& getElement(size_t i) {
      return likeConstVersion(this, &T::getElement, i);
   }
};

প্রথম যুক্তি সর্বদা এই পয়েন্টার হয়। দ্বিতীয়টি হল কল করতে সদস্য ফাংশনের পয়েন্টার। এর পরে অতিরিক্ত যুক্তিগুলির একটি স্বেচ্ছাসেবী পরিমাণ পাস করা যায় যাতে সেগুলি ফাংশনে প্রেরণ করা যায়। বৈকল্পিক টেম্পলেটগুলির কারণে এটির জন্য সি ++ 11 প্রয়োজন।


3
এটা লজ্জাজনক যে আমাদের std::remove_bottom_constসাথে যেতে হবে না std::remove_const
TBBle

আমি এই সমাধানটি পছন্দ করি না কারণ এটি এখনও একটি এম্বেড করে const_cast। আপনি getElementনিজেই একটি টেম্পলেট তৈরি করতে পারেন এবং mpl::conditionalআপনার প্রয়োজন মতো ধরণের বৈশিষ্ট্যগুলি ব্যবহার করতে পারেন , প্রয়োজনে iterators বা constiterators এর মতো । আসল সমস্যাটি হ'ল যখন স্বাক্ষরটির এই অংশটি টেম্প্লেটাইজ করা যায় না তখন কোনও পদ্ধতির কনস্টের সংস্করণ কীভাবে তৈরি করা যায়?
v.oddou

2
@ ভি.ডডু: std::remove_const<int const&>এটি int const &(শীর্ষ স্তরের constযোগ্যতা অপসারণ ), সুতরাং NonConst<T>এই উত্তরের জিমন্যাস্টিকস । পটিটিভ std::remove_bottom_constনীচের স্তরের constযোগ্যতা সরিয়ে ফেলতে পারে এবং NonConst<T>এখানে যা করে তা সুনির্দিষ্টভাবে করতে পারে: std::remove_bottom_const<int const&>::type=> int&
TBBle

4
এই সমাধানটি যদি getElementখুব বেশি বোঝা হয় তবে ভাল কাজ করে না । তারপরে স্পষ্টভাবে টেম্পলেট প্যারামিটারগুলি না দিয়ে ফাংশন পয়েন্টারটি সমাধান করা যায় না। কেন?
জন

1
সি ++ 11 নিখুঁত ফরোয়ার্ডিংয়ের জন্য আপনাকে নিজের উত্তরটি ঠিক করতে হবে: likeConstVersion(TObj const* obj, TConstReturn (TObj::*memFun)(TArgs...) const, TArgs&&... args) { return const_cast<typename NonConst<TConstReturn>::type>((obj->*memFun)(std::forward<TArgs>(args)...)); }সম্পূর্ণ: gist.github.com/BlueSolei/bca26a8590265492e2f2760d3cefcf83
শালফ

22

মায়ার্সের চেয়ে কিছুটা ভারবস, তবে আমি এটি করতে পারি:

class X {

    private:

    // This method MUST NOT be called except from boilerplate accessors.
    Z &_getZ(size_t index) const {
        return something;
    }

    // boilerplate accessors
    public:
    Z &getZ(size_t index)             { return _getZ(index); }
    const Z &getZ(size_t index) const { return _getZ(index); }
};

ব্যক্তিগত পদ্ধতিতে এটি একটি অনাকাঙ্ক্ষিত সম্পত্তি রয়েছে যা এটি একটি নন-কনস্ট্যান্ট জেড & একটি কনস্ট কনস্ট্যান্টের জন্য প্রদান করে, যার কারণে এটি ব্যক্তিগত। ব্যক্তিগত পদ্ধতিগুলি বাহ্যিক ইন্টারফেসের আক্রমণকারীদের ভেঙে ফেলতে পারে (এক্ষেত্রে কাঙ্ক্ষিত ইনগ্রেন্টটি "এটির একটিতে থাকা অবজেক্টগুলিতে তার মাধ্যমে প্রাপ্ত রেফারেন্সের মাধ্যমে কোনও কনট অবজেক্টটি পরিবর্তন করা যায় না")।

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

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

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

সংকলকটি সম্পর্কে সেই পরবর্তী পয়েন্টটি মায়ার্সের প্রস্তাবিত প্যাটার্নের ক্ষেত্রেও সত্য, তবে একটি অ-সুস্পষ্ট কনস্ট_কাস্ট সম্পর্কে প্রথম পয়েন্টটি নয়। সুতরাং ভারসাম্য বজায় রেখে আমি আমার মনে হয় যে যদি _getZ তার রিটার্ন মানটির জন্য কনস্ট_কাস্টের প্রয়োজন হয় তবে এই প্যাটার্নটি মায়ার্সের তুলনায় এর মান অনেক হারিয়ে ফেলে। যেহেতু এটি মায়ার্সের তুলনায় অসুবিধাগুলিও ভোগ করে, তাই আমি মনে করি আমি সেই পরিস্থিতিতে তার দিকে চলে যাব। একের থেকে অন্যের কাছে রিফ্যাকচারিং সহজ - এটি কেবলমাত্র অবৈধ কোড এবং বয়লারপ্লেট _getZ কল করে since ক্লাসে এটি অন্য কোনও বৈধ কোডকে প্রভাবিত করে না]]


3
এটির মধ্যে এখনও সমস্যা রয়েছে যে আপনি ফিরে আসা জিনিসটি এক্স এর ধ্রুবক দৃষ্টান্তের জন্য স্থির থাকতে পারে In সেক্ষেত্রে আপনার এখনও _getZ (...) এ কনস্ট_কাস্ট দরকার require যদি পরবর্তী বিকাশকারীদের দ্বারা এটির অপব্যবহার করা হয় তবে এটি এখনও ইউবির দিকে নিয়ে যেতে পারে। যদি ফিরে আসা জিনিসটি 'পরিবর্তনযোগ্য' হয় তবে এটি একটি ভাল সমাধান।
কেভিন

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

13
-1: এটি অনেক পরিস্থিতিতে কাজ করে না। কি হবে যদি somethingএর মধ্যে _getZ()একটি দৃষ্টান্ত পরিবর্তনশীল কার্যকারিতা রয়েছে? সংকলক (বা কমপক্ষে কিছু সংকলক) অভিযোগ করবে যেহেতু _getZ()কনস্ট, তাই কোনও উদাহরণের মধ্যে উল্লেখযোগ্য পরিবর্তনশীল কনস্টেরও হয়। সুতরাং somethingকনস্ট হবে (এটি টাইপ হবে const Z&) এবং রূপান্তর করা যায় নি Z&। আমার (স্বীকারোক্তি কিছুটা সীমাবদ্ধ) অভিজ্ঞতায় বেশিরভাগ সময় somethingএইরকম ক্ষেত্রে একটি উদাহরণ পরিবর্তনশীল।
মাধ্যাকর্ষণ

2
@ গ্র্যাভিটিব্রিংগার: তারপরে "কিছু" জড়িত হওয়া দরকার const_cast। কনস্টের অবজেক্ট থেকে নন-কনস্ট্যান্ট রিটার্ন পাওয়ার জন্য কোডটির জন্য প্লেস-হোল্ডার হওয়ার উদ্দেশ্য ছিল, নকল করা গিটারে যা থাকত তা স্থানধারক হিসাবে নয় । সুতরাং "কিছু" কেবল একটি উদাহরণ পরিবর্তনশীল নয়।
স্টিভ জেসপ

2
আমি দেখি. যদিও এটি কৌশলটির কার্যকারিতা হ্রাস করে। আমি ডাউনভোটটি সরিয়ে ফেলব, তবে এসও আমাকে ছাড়তে দেবে না।
মাধ্যাকর্ষণ

22

সুন্দর প্রশ্ন এবং সুন্দর উত্তর। আমার আরও একটি সমাধান রয়েছে, এতে কোনও কাস্ট ব্যবহার করা হয়নি:

class X {

private:

    std::vector<Z> v;

    template<typename InstanceType>
    static auto get(InstanceType& instance, std::size_t i) -> decltype(instance.get(i)) {
        // massive amounts of code for validating index
        // the instance variable has to be used to access class members
        return instance.v[i];
    }

public:

    const Z& get(std::size_t i) const {
        return get(*this, i);
    }

    Z& get(std::size_t i) {
        return get(*this, i);
    }

};

তবে এটিতে একটি স্থির সদস্যের প্রয়োজন এবং এটি ব্যবহারের প্রয়োজনের কদর্যতা রয়েছে instance ভিতরে ভেরিয়েবলটি ।

আমি এই সমাধানের সমস্ত সম্ভাব্য (নেতিবাচক) প্রভাবগুলি বিবেচনা করি নি। দয়া করে আমাকে জানান


4
ভাল, আপনি আরও বয়লারপ্লেট যুক্ত করেছেন এমন সাধারণ সত্যের সাথে চলুন। যদি কিছু হয় তবে ভাষাটি কেন ফিরতি ধরণের পাশাপাশি ফাংশন কোয়ালিফায়ার সংশোধন করার জন্য একটি পদ্ধতির প্রয়োজন তা উদাহরণ হিসাবে ব্যবহার করা উচিত auto get(std::size_t i) -> auto(const), auto(&&)। '&&' কেন? আহ, তাই আমি বলতে পারি:auto foo() -> auto(const), auto(&&) = delete;
কেএফসনে

gd1: ঠিক আমার মনে ছিল কি। @kfsone এবং ঠিক আমিও কী শেষ করেছি।
v.oddou

1
@kfsone সিনট্যাক্সে thisকীওয়ার্ড যুক্ত করা উচিত । আমি প্রস্তাব দিচ্ছি template< typename T > auto myfunction(T this, t args) -> decltype(ident)এই কীওয়ার্ডটি অন্তর্নিহিত অবজেক্ট ইনস্ট্যান্স আর্গুমেন্ট হিসাবে স্বীকৃত হবে এবং সংকলকটি স্বীকৃতি দেয় যে মাইফংশানটি একজন সদস্য বা TTকল সাইটে স্বয়ংক্রিয়ভাবে ছাড় করা হবে, যা সর্বদা শ্রেণীর ধরণ, তবে বিনামূল্যে সিভি যোগ্যতার সাথে থাকবে।
v.oddou

2
যে সমাধান এছাড়াও সুবিধা (বনাম হয়েছে const_castএকটি করে) ফিরে যাওয়ার অনুমতি দিতে iteratorএবং const_iterator
জারোড 42

1
যদি বাস্তবায়ন সিপিপি ফাইলে স্থানান্তরিত হয় (এবং নকল না করার পদ্ধতিটি তুচ্ছ না হওয়া উচিত, এটি সম্ভবত কেস হবে), staticশ্রেণি স্কোপের পরিবর্তে ফাইল স্কোপে করা যেতে পারে। :-)
জারোড 42

8

আপনি এটি টেমপ্লেটগুলি দিয়েও সমাধান করতে পারেন। এই সমাধানটি সামান্য কুরুচিপূর্ণ (তবে কদর্যতা .cpp ফাইলে লুকানো রয়েছে) তবে এটি দৃ const়তার সংকলক পরীক্ষা করে এবং কোনও কোড নকল করে না।

.h ফাইল:

#include <vector>

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    const std::vector<Z>& GetVector() const { return vecZ; }
    std::vector<Z>& GetVector() { return vecZ; }

    Z& GetZ( size_t index );
    const Z& GetZ( size_t index ) const;
};

.cpp ফাইল:

#include "constnonconst.h"

template< class ParentPtr, class Child >
Child& GetZImpl( ParentPtr parent, size_t index )
{
    // ... massive amounts of code ...

    // Note you may only use methods of X here that are
    // available in both const and non-const varieties.

    Child& ret = parent->GetVector()[index];

    // ... even more code ...

    return ret;
}

Z& X::GetZ( size_t index )
{
    return GetZImpl< X*, Z >( this, index );
}

const Z& X::GetZ( size_t index ) const
{
    return GetZImpl< const X*, const Z >( this, index );
}

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

[সম্পাদনা করুন: পরীক্ষার সময় যুক্ত করা সিএসডিডিও অন্তর্ভুক্ত অপ্রয়োজনীয় অপসারণ করা হয়েছে]]


3
আপনি ব্যক্তিগত সদস্যদের অ্যাক্সেস পাওয়ার জন্য জটিল প্রয়োগটি সর্বদা স্থিতিশীল সদস্য হিসাবে তৈরি করতে পারেন। ফাংশনটি কেবল শ্রেণির শিরোনাম ফাইলটিতেই ঘোষণা করা দরকার, সংজ্ঞাটি শ্রেণি প্রয়োগকারী ফাইলের মধ্যে থাকতে পারে। এটি সর্বোপরি শ্রেণি বাস্তবায়নের অংশ।
সিবি বেইলি

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

এই সমাধানটির জন্য +1 যা কোনও কোড নকল করে না, বা কোনও কুৎসিত ব্যবহার করে না const_cast(যা দুর্ঘটনাক্রমে এমন কোনও জিনিসকে নষ্ট করতে ব্যবহার করা যেতে পারে যা আসলে এমন কিছু নয় যা বোঝার মতো নয়)।
হ্যালো গুডবাই

আজকাল এটি টেম্পলেটটির জন্য একটি অনুমিত রিটার্ন টাইপের সাথে সরল করা যেতে পারে (বিশেষত এটি দরকারী যেহেতু এটি সদস্যের ক্ষেত্রে ক্লাসে নকল করতে হবে তা হ্রাস করে)।
ডেভিস হেরিং

3

কীভাবে যুক্তিটিকে একটি ব্যক্তিগত পদ্ধতিতে সরানো, এবং কেবল "রেফারেন্স পান এবং ফিরে পাবেন" কীভাবে জিনিসগুলি পাওয়া যায়? প্রকৃতপক্ষে, আমি একটি সরল গেটর ফাংশনের অভ্যন্তরে স্থির এবং দৃ !় ক্যাসেট সম্পর্কে মোটামুটি বিভ্রান্ত হব এবং আমি অত্যন্ত বিরল পরিস্থিতি ব্যতীত সেই কুৎসিত বিবেচনা করব!


অপরিবর্তিত আচরণ এড়াতে আপনার এখনও একটি কনস্টাস্টের প্রয়োজন। মার্টিন ইয়র্ক এর উত্তর এবং সেখানে আমার মন্তব্য দেখুন।
কেভিন

1
কেভিন, মার্টিন ইয়র্কের কী জবাব
পিটার নিম্মো

2

এটি কি প্রিপ্রসেসর ব্যবহার করে প্রতারণা করছে?

struct A {

    #define GETTER_CORE_CODE       \
    /* line 1 of getter code */    \
    /* line 2 of getter code */    \
    /* .....etc............. */    \
    /* line n of getter code */       

    // ^ NOTE: line continuation char '\' on all lines but the last

   B& get() {
        GETTER_CORE_CODE
   }

   const B& get() const {
        GETTER_CORE_CODE
   }

   #undef GETTER_CORE_CODE

};

এটি টেমপ্লেট বা কাস্টের মতো অভিনব নয়, তবে এটি আপনার অভিপ্রায় তৈরি করে ("এই দুটি ফাংশনটি একইরকম হওয়া উচিত") বেশ সুস্পষ্ট।


1
তবে আপনাকে ব্যাকস্ল্যাশগুলিতে সতর্কতা অবলম্বন করতে হবে (মাল্টলাইন ম্যাক্রোগুলির জন্য যথারীতি) এবং এ ছাড়া আপনি বেশিরভাগ (সমস্ত না থাকলে) সম্পাদকগুলিতে সিনট্যাক্স হাইলাইটিং হারাবেন।
রুসলান

2

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

আমি একটি ম্যাক্রো লিখেছিলাম FROM_CONST_OVERLOAD() যা কনস্ট্যান্ট ফাংশনটি শুরু করতে নন-কনস্ট্যান্ট ফাংশনে রাখা যেতে পারে।

ব্যবহারের উদাহরণ:

class MyClass
{
private:
    std::vector<std::string> data = {"str", "x"};

public:
    // Works for references
    const std::string& GetRef(std::size_t index) const
    {
        return data[index];
    }

    std::string& GetRef(std::size_t index)
    {
        return FROM_CONST_OVERLOAD( GetRef(index) );
    }


    // Works for pointers
    const std::string* GetPtr(std::size_t index) const
    {
        return &data[index];
    }

    std::string* GetPtr(std::size_t index)
    {
        return FROM_CONST_OVERLOAD( GetPtr(index) );
    }
};

সহজ এবং পুনরায় ব্যবহারযোগ্য প্রয়োগ:

template <typename T>
T& WithoutConst(const T& ref)
{
    return const_cast<T&>(ref);
}

template <typename T>
T* WithoutConst(const T* ptr)
{
    return const_cast<T*>(ptr);
}

template <typename T>
const T* WithConst(T* ptr)
{
    return ptr;
}

#define FROM_CONST_OVERLOAD(FunctionCall) \
  WithoutConst(WithConst(this)->FunctionCall)

ব্যাখ্যা:

অনেক উত্তরে পোস্ট হিসাবে, একটি অ-সদস্য সদস্য ফাংশনে কোড নকল এড়ানোর জন্য সাধারণ প্যাটার্নটি হ'ল:

return const_cast<Result&>( static_cast<const MyClass*>(this)->Method(args) );

টাইপ ইনফারেন্স ব্যবহার করে এই প্রচুর বয়লারপ্লেট এড়ানো যায়। প্রথমত, const_castএ encapsulated করা যেতে পারে WithoutConst(), যা তার যুক্তি ধরণ infers এবং const-কোয়ালিফায়ার সরিয়ে ফেলা হয়। দ্বিতীয়ত, অনুরূপ পয়েন্টারটি পয়েন্টারটির WithConst()যোগ্যতা অর্জনের জন্য ব্যবহার করা যেতে পারে this, যা কনস্ট-ওভারলোডেড পদ্ধতিটিকে কল করতে সক্ষম করে।

বাকিটি হ'ল একটি সরল ম্যাক্রো যা সঠিকভাবে যোগ্যতার সাথে কলটির উপসর্গ করে this->এবং ফলাফলটি থেকে কনটকে সরিয়ে দেয়। যেহেতু ম্যাক্রোতে ব্যবহৃত অভিব্যক্তিটি প্রায় সর্বদা 1: 1 ফরোয়ার্ড আর্গুমেন্ট সহ একটি সাধারণ ফাংশন কল তাই একাধিক মূল্যায়নের মতো ম্যাক্রোগুলির ঘাটতি কিক করে না The__VA_ARGS__ এছাড়াও ব্যবহার করা যেতে পারে, কিন্তু কমা (হিসাবে প্রয়োজন করা উচিত নয় আর্গুমেন্ট বিভাজক) প্রথম বন্ধনী মধ্যে ঘটে।

এই পদ্ধতির বিভিন্ন সুবিধা রয়েছে:

  • সর্বনিম্ন এবং প্রাকৃতিক বাক্য গঠন - কেবল কলটি মোড়ানো FROM_CONST_OVERLOAD( )
  • কোনও অতিরিক্ত সদস্যের প্রয়োজন নেই
  • সি ++ 98 এর সাথে সামঞ্জস্যপূর্ণ
  • সরল বাস্তবায়ন, কোনও টেম্পলেট বিপণন এবং শূন্য নির্ভরতা নেই
  • এক্সটেনসেবল: অন্যান্য const সম্পর্ক যোগ করা যেতে পারে (যেমন const_iterator, std::shared_ptr<const T>, ইত্যাদি)। এটির WithoutConst()জন্য, সম্পর্কিত ধরণের জন্য কেবল ওভারলোড ।

সীমাবদ্ধতা: এই সমাধানটি দৃশ্যের জন্য অনুকূলিত হয়েছে যেখানে কনস্ট্যান্ট ওভারলোডের মতো কনস্ট্যান্ট ওভারলোড ঠিক একই কাজ করছে, যাতে যুক্তিগুলি 1: 1 এগিয়ে যেতে পারে। যদি আপনার যুক্তিটি পৃথক হয় এবং আপনি কনস্টের সংস্করণটির মাধ্যমে কল করছেন না this->Method(args), আপনি অন্যান্য পদ্ধতির বিষয়ে বিবেচনা করতে পারেন।


2

তাদের জন্য (আমার মতো) যারা

  • সি ++ 17 ব্যবহার করুন
  • ন্যূনতম পরিমাণে বয়লারপ্লেট / পুনরাবৃত্তি এবং যোগ করতে চান
  • ম্যাক্রো (মেটা-ক্লাসের জন্য অপেক্ষা করার সময় ...) ব্যবহার করে কিছু মনে করবেন না ,

এখানে অন্য গ্রহণ করা হয়:

#include <utility>
#include <type_traits>

template <typename T> struct NonConst;
template <typename T> struct NonConst<T const&> {using type = T&;};
template <typename T> struct NonConst<T const*> {using type = T*;};

#define NON_CONST(func)                                                     \
    template <typename... T> auto func(T&&... a)                            \
        -> typename NonConst<decltype(func(std::forward<T>(a)...))>::type   \
    {                                                                       \
        return const_cast<decltype(func(std::forward<T>(a)...))>(           \
            std::as_const(*this).func(std::forward<T>(a)...));              \
    }

এটি মূলত @Peit, @ ডেভিডস্টোন এবং @ sh1 ( EDIT : এবং @cdowie এর উন্নতি) এর উত্তরগুলির মিশ্রণ । এটি টেবিলটিতে কী যুক্ত করে তা হল আপনি কেবলমাত্র একটি অতিরিক্ত লাইন কোড থেকে বেরিয়ে যাবেন যা কেবলমাত্র ফাংশনটির নাম দেয় (তবে কোনও যুক্তি বা রিটার্নের ধরণের নকল নয়):

class X
{
    const Z& get(size_t index) const { ... }
    NON_CONST(get)
};

দ্রষ্টব্য: জিসিসি এটি 8.1 এর আগে সংকলন করতে ব্যর্থ, ঝনঝন -5 এবং উপরের দিকে পাশাপাশি এমএসভিসি -19 খুশি ( সংকলক এক্সপ্লোরার অনুসারে )।


এটি কেবল আমার জন্য সরাসরি কাজ করেছে। এটি একটি দুর্দান্ত উত্তর, ধন্যবাদ!
সংক্ষিপ্ত

আমাদেরও যে ওভারলোড হয়েছে তার ক্ষেত্রে বিভিন্ন ধরণের রেফারেন্স রয়েছে সে ক্ষেত্রে আমরা সঠিক রিটার্ন টাইপটি ব্যবহার করছি তা নিশ্চিত করার জন্য কি যুক্তিগুলি decltype()ব্যবহার std::forwardকরা উচিত নয় get()?
সিডিউইউই

@cdowie আপনি একটি উদাহরণ প্রদান করতে পারেন?
axxel

@ অ্যাক্সেক্সেল এটি নরক হিসাবে স্বীকৃত, তবে আপনি এখানে যানNON_CONSTম্যাক্রো deduces রিটার্ন টাইপ ভুল এবং const_castএ ফরওয়ার্ডিং অভাবে ভুল টাইপ গুলি decltype(func(a...))ধরনের। তাদের প্রতিস্থাপন এটি decltype(func(std::forward<T>(a)...)) সমাধান করে । (এখানে কেবলমাত্র একটি লিঙ্কারের ত্রুটি আছে কারণ আমি কখনই ঘোষিত X::getওভারলোডগুলির কোনও সংজ্ঞা
দিইনি

1
ধন্যবাদ @ সিডিউউই, আমি অ- কনস্ট্যান্ট
axxel

1

এখানে এবং alচ্ছিক SFINAE পরীক্ষার সাথে টেম্পলেট স্ট্যাটিক সহায়ক ফাংশনের একটি সি ++ 17 সংস্করণ।

#include <type_traits>

#define REQUIRES(...)         class = std::enable_if_t<(__VA_ARGS__)>
#define REQUIRES_CV_OF(A,B)   REQUIRES( std::is_same_v< std::remove_cv_t< A >, B > )

class Foobar {
private:
    int something;

    template<class FOOBAR, REQUIRES_CV_OF(FOOBAR, Foobar)>
    static auto& _getSomething(FOOBAR& self, int index) {
        // big, non-trivial chunk of code...
        return self.something;
    }

public:
    auto& getSomething(int index)       { return _getSomething(*this, index); }
    auto& getSomething(int index) const { return _getSomething(*this, index); }
};

সম্পূর্ণ সংস্করণ: https://godbolt.org/z/mMK4r3


1

আমি এমন ম্যাক্রো নিয়ে এসেছি যা স্বয়ংক্রিয়ভাবে জোড়া / কনস্ট্যান্ট ফাংশন তৈরি করে।

class A
{
    int x;    
  public:
    MAYBE_CONST(
        CV int &GetX() CV {return x;}
        CV int &GetY() CV {return y;}
    )

    //   Equivalent to:
    // int &GetX() {return x;}
    // int &GetY() {return y;}
    // const int &GetX() const {return x;}
    // const int &GetY() const {return y;}
};

বাস্তবায়নের জন্য উত্তরের শেষে দেখুন।

এর যুক্তিটি MAYBE_CONSTসদৃশ। প্রথম অনুলিপিতে, CVকিছুই দিয়ে প্রতিস্থাপন করা হয়; এবং দ্বিতীয় অনুলিপিতে এটি প্রতিস্থাপন করা হয়েছে const

CVম্যাক্রোর যুক্তিতে কতবার উপস্থিত হতে পারে তার কোনও সীমা নেই ।

যদিও কিছুটা অসুবিধা আছে। যদি CVপ্রথম বন্ধনীগুলির অভ্যন্তরে উপস্থিত হয়, এই প্রথম বন্ধনীটির সাথে অবশ্যই উপসর্গ করা উচিত CV_IN:

// Doesn't work
MAYBE_CONST( CV int &foo(CV int &); )

// Works, expands to
//         int &foo(      int &);
//   const int &foo(const int &);
MAYBE_CONST( CV int &foo CV_IN(CV int &); )

বাস্তবায়ন:

#define MAYBE_CONST(...) IMPL_CV_maybe_const( (IMPL_CV_null,__VA_ARGS__)() )
#define CV )(IMPL_CV_identity,
#define CV_IN(...) )(IMPL_CV_p_open,)(IMPL_CV_null,__VA_ARGS__)(IMPL_CV_p_close,)(IMPL_CV_null,

#define IMPL_CV_null(...)
#define IMPL_CV_identity(...) __VA_ARGS__
#define IMPL_CV_p_open(...) (
#define IMPL_CV_p_close(...) )

#define IMPL_CV_maybe_const(seq) IMPL_CV_a seq IMPL_CV_const_a seq

#define IMPL_CV_body(cv, m, ...) m(cv) __VA_ARGS__

#define IMPL_CV_a(...) __VA_OPT__(IMPL_CV_body(,__VA_ARGS__) IMPL_CV_b)
#define IMPL_CV_b(...) __VA_OPT__(IMPL_CV_body(,__VA_ARGS__) IMPL_CV_a)

#define IMPL_CV_const_a(...) __VA_OPT__(IMPL_CV_body(const,__VA_ARGS__) IMPL_CV_const_b)
#define IMPL_CV_const_b(...) __VA_OPT__(IMPL_CV_body(const,__VA_ARGS__) IMPL_CV_const_a)

প্রাক-সি ++ 20 বাস্তবায়ন যা সমর্থন করে না CV_IN:

#define MAYBE_CONST(...) IMPL_MC( ((__VA_ARGS__)) )
#define CV ))((

#define IMPL_MC(seq) \
    IMPL_MC_end(IMPL_MC_a seq) \
    IMPL_MC_end(IMPL_MC_const_0 seq)

#define IMPL_MC_identity(...) __VA_ARGS__
#define IMPL_MC_end(...) IMPL_MC_end_(__VA_ARGS__)
#define IMPL_MC_end_(...) __VA_ARGS__##_end

#define IMPL_MC_a(elem) IMPL_MC_identity elem IMPL_MC_b
#define IMPL_MC_b(elem) IMPL_MC_identity elem IMPL_MC_a
#define IMPL_MC_a_end
#define IMPL_MC_b_end

#define IMPL_MC_const_0(elem)       IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a(elem) const IMPL_MC_identity elem IMPL_MC_const_b
#define IMPL_MC_const_b(elem) const IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a_end
#define IMPL_MC_const_b_end

0

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


2
এটি বেশিরভাগ সময় সত্য হতে পারে। তবে ব্যতিক্রমও রয়েছে।
কেভিন

1
যাইহোক যাইহোক, একটি কনস্টেট সেটারের বেশি অর্থ হয় না;)
jwfearn

আমি বুঝিয়েছি যে নন-কনস্ট্যান্ট গেটর কার্যকরভাবে একটি সেটটার। :)
ডিমা

0

আমি এটি এমন এক বন্ধুর জন্য করেছি যিনি যথাযথভাবে ব্যবহারের ন্যায্যতা প্রমাণ করেছেন const_cast... এটি সম্পর্কে না জেনে আমি সম্ভবত এরকম কিছু করতাম (সত্যিই মার্জিত নয়):

#include <iostream>

class MyClass
{

public:

    int getI()
    {
        std::cout << "non-const getter" << std::endl;
        return privateGetI<MyClass, int>(*this);
    }

    const int getI() const
    {
        std::cout << "const getter" << std::endl;
        return privateGetI<const MyClass, const int>(*this);
    }

private:

    template <class C, typename T>
    static T privateGetI(C c)
    {
        //do my stuff
        return c._i;
    }

    int _i;
};

int main()
{
    const MyClass myConstClass = MyClass();
    myConstClass.getI();

    MyClass myNonConstClass;
    myNonConstClass.getI();

    return 0;
}

0

আমি এই জাতীয় বেসরকারী সহায়ক স্থির ফাংশন টেম্পলেট পরামর্শ দেব:

class X
{
    std::vector<Z> vecZ;

    // ReturnType is explicitly 'Z&' or 'const Z&'
    // ThisType is deduced to be 'X' or 'const X'
    template <typename ReturnType, typename ThisType>
    static ReturnType Z_impl(ThisType& self, size_t index)
    {
        // massive amounts of code for validating index
        ReturnType ret = self.vecZ[index];
        // even more code for determining, blah, blah...
        return ret;
    }

public:
    Z& Z(size_t index)
    {
        return Z_impl<Z&>(*this, index);
    }
    const Z& Z(size_t index) const
    {
        return Z_impl<const Z&>(*this, index);
    }
};

-1

এই ডিডিজে নিবন্ধটি টেমপ্লেট বিশেষায়নের ব্যবহারের একটি উপায় দেখায় যা আপনার কনস্ট_কাস্ট ব্যবহার করার প্রয়োজন নেই। যেমন একটি সাধারণ ফাংশন জন্য এটি যদিও প্রয়োজন হয় না।

বুস্ট :: যে কোনও কাস্টম (এক পর্যায়ে, এটি আর হয় না) অনুলিপি এড়ানোর জন্য কনস্ট্যান্ট সংস্করণটি কল করে কনস্ট্যান্ড সংস্করণ থেকে একটি কনস্টেস্ট ব্যবহার করে। আপনি কনস্ট্যান্ট সংস্করণে কনস্ট কনটেন্টস চাপিয়ে দিতে পারবেন না যদিও আপনাকে খুব হতে হবে be সাথে সাবধানতা অবলম্বন ।

শেষ পর্যন্ত কিছু কোড অনুলিপি হয় ঠিক আছে যতদিন দুটি স্নিপেট একে অপরের উপরে সরাসরি হয়।


ডিডিজে নিবন্ধটি পুনরাবৃত্তিকারীদের বোঝায় - যা প্রশ্নের সাথে প্রাসঙ্গিক নয়। কনস্ট-ইটারেটর ধ্রুবক ডেটা নয় - এগুলি পুনরাবৃত্তিকারী যা ধ্রুবক ডেটার দিকে নির্দেশ করে।
কেভিন

-1

Jwfearn এবং কেভিন প্রদত্ত সমাধানটিতে যুক্ত করতে, ফাংশনটি ভাগ করে দেওয়া_প্টারের সাথে সম্পর্কিত সমাধানটি এখানে দেওয়া হয়েছে:

struct C {
  shared_ptr<const char> get() const {
    return c;
  }
  shared_ptr<char> get() {
    return const_pointer_cast<char>(static_cast<const C &>(*this).get());
  }
  shared_ptr<char> c;
};

-1

আমি যা খুঁজছিলাম তা খুঁজে পেলাম না, তাই আমি আমার নিজের দু'টিকে ঘুরিয়ে দিয়েছি ...

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

struct C {
  int x[10];

  int const* getp() const { return x; }
  int const* getp(int i) const { return &x[i]; }
  int const* getp(int* p) const { return &x[*p]; }

  int const& getr() const { return x[0]; }
  int const& getr(int i) const { return x[i]; }
  int const& getr(int* p) const { return x[*p]; }

  template<typename... Ts>
  auto* getp(Ts... args) {
    auto const* p = this;
    return const_cast<int*>(p->getp(args...));
  }

  template<typename... Ts>
  auto& getr(Ts... args) {
    auto const* p = this;
    return const_cast<int&>(p->getr(args...));
  }
};

যদি আপনার কাছে constপ্রতি নামমাত্র একটি পদ্ধতি থাকে তবে নকল করার জন্য এখনও প্রচুর পদ্ধতি রয়েছে তবে আপনি এটি পছন্দ করতে পারেন:

  template<typename T, typename... Ts>
  auto* pwrap(T const* (C::*f)(Ts...) const, Ts... args) {
    return const_cast<T*>((this->*f)(args...));
  }

  int* getp_i(int i) { return pwrap(&C::getp_i, i); }
  int* getp_p(int* p) { return pwrap(&C::getp_p, p); }

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

  template<typename... Ts>
  auto* getp(Ts... args) { return pwrap<int, Ts...>(&C::getp, args...); }

কিন্তু constপদ্ধতির রেফারেন্স আর্গুমেন্টগুলি টেমপ্লেটে আপাতদৃষ্টিতে বাই-মান তর্কগুলির সাথে মেলে না এবং এটি ভেঙে যায়। নিশ্চিত কেন। এখানে কেন

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