কেন std :: ফাংশন ওভারলোড রেজোলিউশনে অংশ নেয় না?


17

আমি জানি যে নিম্নলিখিত কোডগুলি সংকলন করবে না।

void baz(int i) { }
void baz() {  }


class Bar
{
    std::function<void()> bazFn;
public:
    Bar(std::function<void()> fun = baz) : bazFn(fun){}

};

int main(int argc, char **argv)
{
    Bar b;
    return 0;
}

কারণ std::functionওভারলোড রেজোলিউশনটি বিবেচনা না করার কথা বলা হয়েছে, যেমন আমি এই অন্যান্য পোস্টে পড়েছি ।

প্রযুক্তিগত সীমাবদ্ধতাগুলি এই ধরণের সমাধানকে বাধ্য করেছিল তা আমি পুরোপুরি বুঝতে পারি না।

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


1
@ এভিজি তবে একটি মাত্র ওভারলোড রয়েছে যা ওএস উদাহরণগুলিতে স্বীকৃতি লাভ করতে পারে। আপনার উদাহরণে উভয়ই একটি মিল তৈরি করবে
idclev 463035818

উত্তর:


13

"অনুবাদের পর্যায়ক্রমে" এর সাথে আসলে কিছুই করার নেই। এটি খাঁটি নির্মাতাদের সম্পর্কে std::function

দেখুন, std::function<R(Args)>প্রদত্ত ফাংশনটি হুবহু ধরণের প্রয়োজন হয় না R(Args)। বিশেষত, এটির জন্য এটি কোনও ফাংশন পয়েন্টার দেওয়ার প্রয়োজন হয় না। এটা কোনো callable প্রকার (সদস্য ফাংশন পয়েন্টার, কিছু বস্তুর একটি জমিদার আছে লাগতে পারে operator()এতক্ষণ এটা invokable হিসাবে) যেন তা নিয়ে Argsপরামিতি এবং আয় কিছু করতে পরিবর্তনীয় R (বা যদি Rহয় void, এটা কিছু ফেরত পারেন)।

যে কাজের জন্য, যথাযথ কন্সট্রাকটর std::functionআবশ্যক হতে টেমপ্লেট : template<typename F> function(F f);। এটি, এটি কোনও ফাংশনের ধরণ নিতে পারে (উপরের সীমাবদ্ধতার অধীন)।

অভিব্যক্তি bazএকটি ওভারলোড সেট উপস্থাপন করে। আপনি যদি ওভারলোড সেটটি কল করতে সেই অভিব্যক্তিটি ব্যবহার করেন তবে তা ঠিক। আপনি যদি কোনও ক্রিয়াকলাপটি নির্দিষ্ট ফাংশন পয়েন্টার গ্রহণের কোনও পরামিতি হিসাবে সেই অভিব্যক্তিটি ব্যবহার করেন, সি ++ ওভারলোড সেটটি একটি একক কলকে হুইটলেট করতে পারে, সুতরাং এটি সূক্ষ্ম হয়।

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


আমি যদি কিছু ভুল হয়ে থাকে তবে আমাকে ক্ষমা করুন, তবে কীভাবে সি ++ এ উপলব্ধ ওভারলোডগুলির মধ্যে পার্থক্য করার ক্ষমতা রাখবে না? আমি এখন দেখতে পাচ্ছি যে std :: ফাংশন <টি> কোনও সামঞ্জস্যপূর্ণ প্রকার গ্রহণ করে, কেবলমাত্র একটি সঠিক মিল নয়, তবে two দুটি বাজ () ওভারলোডগুলির মধ্যে একটিরই অদলবদলযোগ্য যেমন এটি নির্দিষ্ট পরামিতি নিয়েছে। কেন এটি অসম্পূর্ণ করা অসম্ভব?
তুরোরাইজ

কারণ সমস্ত সি ++ দেখতে পাওয়া স্বাক্ষরটি আমি উদ্ধৃত করেছি। এর বিপরীতে কী মিলবে তার কথা তা জানে না। মূলত, আপনি যখনই কোনও ওভারলোড সেট ব্যবহার করেন তখন সি ++ কোড (কোডটি সেই টেমপ্লেট ঘোষণা হিসাবে কোড) থেকে সঠিক উত্তরটি স্পষ্টভাবে কী তা স্পষ্ট নয় , ভাষা আপনাকে কী বোঝাতে চাইছে তা বানান করতে বাধ্য করে।
নিকল বোলাস

1
@ টিউরটয়েজ: functionক্লাস টেম্পলেটটিতে টেমপ্লেট পরামিতি অপ্রাসঙ্গিক । আপনি যে কনস্ট্রাক্টরকে কল করছেন তার টেমপ্লেট প্যারামিটারটি কী গুরুত্বপূর্ণ । যা ঠিক typename F: ওরফে, যে কোনও ধরণের।
নিকল বোলাস

1
@ ট্যুরটাইজ: আমি মনে করি আপনি কিছু ভুল বুঝছেন। এটি "just F" কারণ টেমপ্লেটগুলি কীভাবে এটি কাজ করে। স্বাক্ষর থেকে, সেই ফাংশন কনস্ট্রাক্টর যে কোনও প্রকারের লাগে, সুতরাং এটির সাথে টেমপ্লেট আর্গুমেন্ট ছাড়ের যে কোনও প্রচেষ্টা প্যারামিটার থেকে প্রকারটি কেটে যাবে। কোনও ওভারলোড সেটে ছাড়ের প্রয়োগের যে কোনও প্রয়াস হ'ল সংকলন ত্রুটি। সংকলকটি কোনটি কার্যকর হয় তা দেখতে সেট থেকে প্রতিটি সম্ভাব্য প্রকারকে কমানোর চেষ্টা করে না।
নিকল বোলাস

1
"একটি ওভারলোড সেটে ছাড়ের প্রয়োগের যে কোনও প্রয়াসই একটি সংকলন ত্রুটি। সংকলকটি কোনটি কার্যকর হয় তা দেখতে সেট থেকে প্রতিটি সম্ভাব্য প্রকারকে ছাড় করার চেষ্টা করে না" " ঠিক আমি কী অনুপস্থিত ছিল, আপনাকে ধন্যবাদ :)
TuRtoise

7

ওভারলোড রেজোলিউশনটি তখনই ঘটে যখন আপনি (ক) আপনি কোনও ফাংশন / অপারেটরের নাম কল করছেন বা (খ) এটিকে একটি স্বাক্ষর সহ একটি পয়েন্টারে (ফাংশন বা সদস্য ফাংশন করতে) কাস্ট করছেন।

এখানেও ঘটছে না।

std::functionতার স্বাক্ষরের সাথে সামঞ্জস্যপূর্ণ যে কোনও বস্তু গ্রহণ করে । এটি বিশেষভাবে কোনও ফাংশন পয়েন্টার গ্রহণ করে না। (একটি ল্যাম্বডা একটি স্ট্যান্ড ফাংশন নয়, এবং একটি স্ট্যান্ড ফাংশন ল্যাম্বডা নয়)

এখন আমার হোমব্রু ফাংশনের রূপগুলিতে, স্বাক্ষরের জন্য R(Args...)আমি R(*)(Args...)ঠিক এই কারণে একটি যুক্তি (একটি সঠিক মিল) গ্রহণ করি । তবে এর অর্থ এটি "সামঞ্জস্যপূর্ণ" স্বাক্ষরের উপরে "সঠিক মিল" স্বাক্ষরগুলিকে উন্নীত করে।

মূল সমস্যাটি হ'ল একটি ওভারলোড সেটটি কোনও সি ++ অবজেক্ট নয়। আপনি একটি ওভারলোড সেটটির নাম রাখতে পারেন, তবে আপনি এটিকে "স্থানীয়ভাবে" পাস করতে পারবেন না।

এখন, আপনি এই জাতীয় ফাংশনের সিউডো-ওভারলোড সেট তৈরি করতে পারেন:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

#define OVERLOADS_OF(...) \
  [](auto&&...args) \
  RETURNS( __VA_ARGS__(decltype(args)(args)...) )

এটি একটি একক সি ++ অবজেক্ট তৈরি করে যা কোনও ফাংশন নামে ওভারলোড রেজোলিউশন করতে পারে।

ম্যাক্রোগুলি প্রসারিত করে আমরা পাই:

[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }

যা লিখতে বিরক্তিকর। একটি সহজ, সামান্য কম দরকারী, সংস্করণ এখানে:

[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }

আমাদের কাছে একটি ল্যাম্বডা রয়েছে যা বহু সংখ্যক আর্গুমেন্ট গ্রহণ করে, তারপরে নিখুঁতভাবে এগুলিকে এগিয়ে দেয় baz

তারপর:

class Bar {
  std::function<void()> bazFn;
public:
  Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};

কাজ করে। আমরা ওভারলোড রেজোলিউশনটি যে ল্যাম্বডায় সংরক্ষণ করি তাতে সরাসরি ওভারলোড সেটটি funপাস করার পরিবর্তে fun(যা এটি সমাধান করতে পারে না) স্থির করে।

সি ++ ভাষায় কোনও অপারেশন সংজ্ঞায়িত করার জন্য কমপক্ষে একটি প্রস্তাব এসেছে যা কোনও ফাংশন নামকে ওভারলোড সেট অবজেক্টে রূপান্তর করে। এ জাতীয় মানক প্রস্তাবটি স্ট্যান্ডার্ড না হওয়া পর্যন্ত OVERLOADS_OFম্যাক্রো কার্যকর।

আপনি আরও একধাপ এগিয়ে যেতে এবং কাস্ট-টু-সামঞ্জস্যপূর্ণ-ফাংশন-পয়েন্টারটি সমর্থন করতে পারেন।

struct baz_overloads {
  template<class...Ts>
  auto operator()(Ts&&...ts)const
  RETURNS( baz(std::forward<Ts>(ts)...) );

  template<class R, class...Args>
  using fptr = R(*)(Args...);
  //TODO: SFINAE-friendly support
  template<class R, class...Ts>
  operator fptr<R,Ts...>() const {
    return [](Ts...ts)->R { return baz(std::forward<Ts>(ts)...); };
  }
};

কিন্তু এটি অবসন্নতা পেতে শুরু হয়।

সরাসরি উদাহরণ

#define OVERLOADS_T(...) \
  struct { \
    template<class...Ts> \
    auto operator()(Ts&&...ts)const \
    RETURNS( __VA_ARGS__(std::forward<Ts>(ts)...) ); \
\
    template<class R, class...Args> \
    using fptr = R(*)(Args...); \
\
    template<class R, class...Ts> \
    operator fptr<R,Ts...>() const { \
      return [](Ts...ts)->R { return __VA_ARGS__(std::forward<Ts>(ts)...); }; \
    } \
  }

5

এখানে সমস্যাটি এখানে সংকলকটিকে কীভাবে পয়েন্টার ক্ষয় পর্যন্ত ফাংশনটি করবেন তা বলার অপেক্ষা রাখে না। যদি তোমার থাকে

void baz(int i) { }
void baz() {  }

class Bar
{
    void (*bazFn)();
public:
    Bar(void(*fun)() = baz) : bazFn(fun){}

};

int main(int argc, char **argv)
{
    Bar b;
    return 0;
}

কোডটি কাজ করবে যেহেতু এখনই সংকলক জানে যে আপনি কোন ফাংশনটি চান তাই একটি কংক্রিট টাইপ রয়েছে যা আপনি নির্ধারণ করছেন।

আপনি যখন std::functionএটি ব্যবহার করেন তখন এটি ফাংশন অবজেক্ট কনস্ট্রাক্টর বলে থাকেন যা ফর্মটি রয়েছে

template< class F >
function( F f );

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

Bar(std::function<void()> fun = (void(*)())baz) : bazFn(fun){}

এককভাবে জোর পেতে এবং ছাড়ের অনুমতি দিতে।


"যেহেতু বাজ একটি ওভারলোডেড ফাংশন তাই কোনও একক টাইপই কেটে নেওয়া যায় না" তবে সি ++ 14 "যেহেতু এই কনস্ট্রাক্টর আর্গুমেন্ট আরগস ... এবং রিটার্ন টাইপ আর এর জন্য কলযোগ্য না হয় যদি না ওভারলোড রেজোলিউশনে অংশ নেয় না" আমি নিশ্চিত নয়, তবে আমি প্রত্যাশার নিকটে ছিলাম যে এই অস্পষ্টতা সমাধানের জন্য যথেষ্ট হবে
idclev 463035818

3
@ পূর্বে অজ্ঞাতগুলি 6630৩৩৮৮১৮ তবে এটি নির্ধারণ করার জন্য, প্রথমে এটির জন্য প্রথমে একটি ধরণের প্রয়োজন এবং এটি কোনও ওভারলোডেড নাম হওয়ায় এটি করতে পারে না।
নাথান অলিভার

1

বিন্দুতে সংকলক সিদ্ধান্ত নিচ্ছে যে কোন ওভারলোডটি std::functionকনস্ট্রাক্টরের মধ্যে প্রবেশ করতে হবে এটি সমস্তই জানেন যে std::functionকনস্ট্রাক্টর কোনও প্রকার নিতে প্ররোচিত হয়েছে । এটি উভয়ই ওভারলোড ব্যবহার করে দেখার চেষ্টা করে যে প্রথমটি সংকলন করে না তবে দ্বিতীয়টি করে।

এটির সমাধানের উপায়টি স্পষ্টভাবে সংকলককে বলতে হবে যে আপনি কোনটির সাথে কোন ওভারলোড চান static_cast:

Bar(std::function<void()> fun = static_cast<void(*)()>(baz)) : bazFn(fun){}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.