ফাংশন স্বাক্ষরে আমি কেন std :: সক্ষম_if এড়ানো উচিত


165

স্কট মায়ার্স তার পরবর্তী বই ইসি ++ 11 এর সামগ্রী এবং স্থিতি পোস্ট করেছেন । তিনি লিখেছেন যে বইয়ের একটি আইটেম " std::enable_ifফাংশন স্বাক্ষরে এড়ানো" হতে পারে ।

std::enable_if অতিরিক্ত লোড রেজোলিউশন থেকে শর্তাধীন ফাংশন বা ক্লাস অপসারণ করতে একটি ফাংশন আর্গুমেন্ট হিসাবে, রিটার্ন টাইপের হিসাবে বা ক্লাস টেম্পলেট বা ফাংশন টেম্পলেট প্যারামিটার হিসাবে ব্যবহৃত হতে পারে।

ইন এই প্রশ্নের তিনটি সমাধান দেখানো হয়।

ফাংশন প্যারামিটার হিসাবে:

template<typename T>
struct Check1
{
   template<typename U = T>
   U read(typename std::enable_if<
          std::is_same<U, int>::value >::type* = 0) { return 42; }

   template<typename U = T>
   U read(typename std::enable_if<
          std::is_same<U, double>::value >::type* = 0) { return 3.14; }   
};

টেমপ্লেট প্যারামিটার হিসাবে:

template<typename T>
struct Check2
{
   template<typename U = T, typename std::enable_if<
            std::is_same<U, int>::value, int>::type = 0>
   U read() { return 42; }

   template<typename U = T, typename std::enable_if<
            std::is_same<U, double>::value, int>::type = 0>
   U read() { return 3.14; }   
};

রিটার্ন টাইপ হিসাবে:

template<typename T>
struct Check3
{
   template<typename U = T>
   typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
      return 42;
   }

   template<typename U = T>
   typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
      return 3.14;
   }   
};
  • কোন সমাধানটিকে অগ্রাধিকার দেওয়া উচিত এবং কেন আমি অন্যদের এড়ানো উচিত?
  • কোন ক্ষেত্রে " std::enable_ifফাংশন স্বাক্ষরগুলি এড়ান" রিটার্ন টাইপ হিসাবে ব্যবহারের বিষয়ে উদ্বেগ প্রকাশ করে (যা সাধারণ ফাংশনের স্বাক্ষরের অংশ নয় তবে টেমপ্লেট বিশেষীকরণের)?
  • সদস্য এবং অ-সদস্য ফাংশন টেম্পলেটগুলির জন্য কোনও পার্থক্য রয়েছে?

কারণ ওভারলোডিং ঠিক ততটাই দুর্দান্ত। যদি কিছু হয় তবে এমন একটি বাস্তবায়নের জন্য প্রতিনিধি দিন যা শ্রেণিবদ্ধ টেম্পলেট (বিশেষায়িত) ব্যবহার করে।
শে

সদস্য ফাংশনগুলি পৃথক যে ওভারলোড সেটটিতে বর্তমান ওভারলোডের পরে ঘোষিত ওভারলোডগুলি অন্তর্ভুক্ত রয়েছে । ভ্যারিয়েডিকস বিলম্বিত রিটার্ন টাইপ করার সময় এটি গুরুত্বপূর্ণ (যেখানে
রিটার্নের ধরণটি

1
ওয়েল, নিছক subjectively আমি বলতে যে প্রায়ই বেশ দরকারী হচ্ছে আমি পছন্দ করি না আছে std::enable_ifআমার ফাংশন স্বাক্ষর (বিশেষ করে কুশ্রী অতিরিক্ত বিশৃঙ্খল করতে nullptrফাংশন যুক্তি সংস্করণ) কারণ এটা সবসময় (কিছু জন্য এটি কি মনে হচ্ছে, একটি অদ্ভুত হ্যাক static ifশক্তি আন্তঃবিশ্বের ভাষা বৈশিষ্ট্যটি ব্যবহার করতে টেমপ্লেট কালো-যাদু ব্যবহার করে আরও বেশি সুন্দর এবং পরিষ্কার করুন। এ কারণেই আমি যখনই সম্ভব ট্যাগ-প্রেরণ পছন্দ করি (ভাল, আপনার কাছে এখনও অতিরিক্ত অদ্ভুত যুক্তি রয়েছে, তবে পাবলিক ইন্টারফেসে নয় এবং এর চেয়ে কম কদর্য এবং ক্রিপ্টিকও রয়েছে )।
খ্রিস্টান রাউ

2
আমি কি করে জিজ্ঞাসা করতে চান =0মধ্যে typename std::enable_if<std::is_same<U, int>::value, int>::type = 0সাধা? আমি এটি বুঝতে সঠিক সংস্থান খুঁজে পাইনি। আমি প্রথম অংশ হবার আগেই =0একজন সদস্য টাইপ হয়েছে intযদি Uএবং intএকই। অনেক ধন্যবাদ!
astroboylrx

4
@astroboylrx মজার, আমি এই মন্তব্য করে একটি মন্তব্য দিতে যাচ্ছি। মূলত, এটি = 0 নির্দেশ করে যে এটি একটি ডিফল্ট, অ-টাইপযুক্ত টেম্পলেট প্যারামিটার। এটি এইভাবে করা হয়েছে কারণ ডিফল্ট প্রকারের টেম্পলেট পরামিতিগুলি স্বাক্ষরের অংশ নয়, সুতরাং আপনি সেগুলি ওভারলোড করতে পারবেন না।
নীর ফ্রেডম্যান

উত্তর:


107

টেমপ্লেট পরামিতিগুলিতে হ্যাক রাখুন

enable_ifটেমপ্লেট প্যারামিটার উপর পদ্ধতির অন্যদের উপর অন্তত দুই সুফল রয়েছে:

  • পঠনযোগ্যতা : সক্ষম_আইফের ব্যবহার এবং রিটার্ন / আর্গুমেন্ট প্রকারগুলি একসাথে টাইপনেম ডিসাম্বিগুয়েটারস এবং নেস্টেড টাইপ অ্যাক্সেসগুলিতে একত্রে মিশে যায় না; যদিও বিচ্ছিন্নতা এবং নেস্টেড ধরণের বিশৃঙ্খলাটিকে উলামের টেম্পলেটগুলির সাহায্যে প্রশমিত করা যায়, এটি এখনও দুটি সম্পর্কযুক্ত জিনিসকে একত্রে মার্জ করে would সক্ষম_এফ ব্যবহারটি টেম্পলেট প্যারামিটারগুলির সাথে সম্পর্কিত যা রিটার্নের ধরণের সাথে নয়। টেমপ্লেট পরামিতিগুলিতে এগুলি থাকার অর্থ তারা যা কিছু গুরুত্বপূর্ণ তার নিকটবর্তী হয়;

  • সর্বজনীন প্রয়োগযোগ্যতা : কনস্ট্রাক্টরদের রিটার্নের ধরণ নেই এবং কিছু অপারেটরের অতিরিক্ত যুক্তি থাকতে পারে না, সুতরাং অন্য দুটি বিকল্পের কোনওটিই যেকোন জায়গায় প্রয়োগ করা যায় না। আপনি টেমপ্লেটগুলিতে কেবলমাত্র SFINAE ব্যবহার করতে পারবেন যেহেতু কোনও টেম্পলেট প্যারামিটারে সক্ষম_আইফ স্থাপন করা সর্বত্র কাজ করে।

আমার জন্য, পাঠযোগ্যতার দিকটি এই পছন্দটিতে বড় প্রেরণাদায়ক উপাদান।


4
এখানেFUNCTION_REQUIRES ম্যাক্রো ব্যবহার করা , এটি পড়ার পক্ষে আরও সুন্দর করে তোলে এবং এটি সি ++ 03 সংকলকগুলিতেও কাজ করে এবং এটি রিটার্ন টাইপের ব্যবহারের উপর নির্ভর করে । এছাড়াও, ফাংশন টেমপ্লেট প্যারামিটারগুলিতে ব্যবহার করা ওভারলোডিংয়ের জন্য সমস্যা তৈরি করে, কারণ এখন ফাংশন স্বাক্ষরটি অস্পষ্ট নয় যার কারণে অস্পষ্ট ওভারলোডিং ত্রুটি হয়। enable_ifenable_if
পল ফুলটজ II

3
এটি একটি পুরানো প্রশ্ন, তবে যে কেউ এখনও পড়ছেন: @ পল উত্থাপিত সমস্যার সমাধানটি enable_ifএকটি ডিফল্ট নন-টাইপ টেম্পলেট প্যারামিটার ব্যবহার করা উচিত যা ওভারলোডিংকে অনুমতি দেয়। অর্থাৎ enable_if_t<condition, int> = 0পরিবর্তে typename = enable_if_t<condition>
নীর ফ্রেডম্যান

: প্রায় স্ট্যাটিক-যদি লিঙ্কটি wayback web.archive.org/web/20150726012736/http://flamingdangerzone.com/...
davidbak

@ আর। মার্টিনহো ফার্নান্দেস flamingdangerzoneআপনার মন্তব্যের লিঙ্কটি এখন একটি স্পাইওয়্যার-ইনস্টলিং পৃষ্ঠাতে নিয়ে গেছে বলে মনে হচ্ছে। আমি এটি নিয়ন্ত্রণকারীদের মনোযোগের জন্য পতাকাঙ্কিত করেছি।
নিসপিও

58

std::enable_ifটেমপ্লেট যুক্তি ছাড়ের সময় " সাবস্টিটিশন ব্যর্থতা একটি ত্রুটি নয় " (ওরফে SFINAE) নীতিটির উপর নির্ভর করে । এটি একটি খুব ভঙ্গুর ভাষার বৈশিষ্ট্য এবং এটি সঠিকভাবে পেতে আপনার খুব সতর্ক হওয়া দরকার।

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

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

template<typename T>
T fun(T arg) 
{ 
    return detail::fun(arg, typename some_template_trait<T>::type() ); 
}

namespace detail {
    template<typename T>
    fun(T arg, std::false_type /* dummy */) { }

    template<typename T>
    fun(T arg, std::true_type /* dummy */) {}
}

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


22
ট্যাগ প্রেরণের একটি অসুবিধা আছে যদিও: যদি আপনার কোনও বৈশিষ্ট্য থাকে যা কোনও ফাংশনের উপস্থিতি সনাক্ত করে এবং ট্যাগ ফাংশনটি প্রয়োগ করে সেই ফাংশনটি প্রয়োগ করা হয় তবে এটি সর্বদা সেই সদস্যটিকে উপস্থিত হিসাবে প্রতিবেদন করে এবং ফলস্বরূপ সম্ভাব্য প্রতিস্থাপন ব্যর্থতার পরিবর্তে একটি ত্রুটি ঘটায় । SFINAE প্রাথমিকভাবে প্রার্থী সেটগুলি থেকে ওভারলোডগুলি অপসারণ করার একটি কৌশল এবং ট্যাগ প্রেরণ দুটি (বা আরও বেশি) ওভারলোডের মধ্যে নির্বাচন করার কৌশল technique কার্যকারিতাতে কিছু ওভারল্যাপ রয়েছে তবে তারা সমতুল্য নয়।
আর মার্টিনহো ফার্নান্দেস

@ আর.মার্টিনহো ফার্নান্দেস আপনি একটি সংক্ষিপ্ত উদাহরণ দিতে পারেন এবং কীভাবে enable_ifএটি সঠিক হবে তা চিত্রিত করতে পারেন?
TemplateRex

1
@ আর.মার্টিনহো ফার্নান্দেস আমি মনে করি এই পয়েন্টগুলি ব্যাখ্যা করে একটি পৃথক উত্তর ওপিতে মূল্য যুক্ত করতে পারে। :-) বিটিডাব্লু, এর মতো বৈশিষ্ট্যগুলি লেখাই এমন is_f_ableএকটি বিষয় যা আমি গ্রন্থাগার লেখকদের জন্য একটি কাজ হিসাবে বিবেচনা করি যারা অবশ্যই তাদের সুবিধা দেয় যখন SFINAE ব্যবহার করতে পারে তবে "নিয়মিত" ব্যবহারকারীদের জন্য এবং একটি বৈশিষ্ট্য দেওয়া হলে is_f_ableআমার মনে হয় ট্যাগ প্রেরণ সহজতর।
টেমপ্লেক্স 10

1
@ হানসামাদ আমি আপনার প্রশ্নের সমাধান করে একটি সংক্ষিপ্ত উত্তর পোস্ট করেছি, এবং পরিবর্তে একটি ব্লগ পোস্টে "এসফিনএইট বা এসএফআইএনএইউ না টু" বিষয়টিকে সম্বোধন করব (এটি এই প্রশ্নটিতে কিছুটা অফ-টপিক)। এটি শেষ করার সাথে সাথে আমি বলতে চাইছি।
আর মার্টিনহো ফার্নান্দেস


5

কোন সমাধানটিকে অগ্রাধিকার দেওয়া উচিত এবং কেন আমি অন্যদের এড়ানো উচিত?

  • টেমপ্লেট প্যারামিটার

    • এটি কনস্ট্রাক্টরগুলিতে ব্যবহারযোগ্য।
    • এটি ব্যবহারকারী-সংজ্ঞায়িত রূপান্তর অপারেটরে ব্যবহারযোগ্য।
    • এর জন্য সি ++ 11 বা তার পরে প্রয়োজন।
    • এটি আইএমও, আরও পাঠযোগ্য।
    • এটি সহজেই ভুলভাবে ব্যবহৃত হতে পারে এবং অতিরিক্ত লোডগুলির সাথে ত্রুটি তৈরি করে:

      template<typename T, typename = std::enable_if_t<std::is_same<T, int>::value>>
      void f() {/*...*/}
      
      template<typename T, typename = std::enable_if_t<std::is_same<T, float>::value>>
      void f() {/*...*/} // Redefinition: both are just template<typename, typename> f()
      

    typename = std::enable_if_t<cond>সঠিক পরিবর্তে লক্ষ্য করুনstd::enable_if_t<cond, int>::type = 0

  • রিটার্ন টাইপ:

    • এটি কনস্ট্রাক্টারে ব্যবহার করা যাবে না। (কোনও রিটার্নের ধরণ নেই)
    • এটি ব্যবহারকারী-সংজ্ঞায়িত রূপান্তর অপারেটরে ব্যবহার করা যাবে না। (ছাড়যোগ্য নয়)
    • এটি প্রাক-সি ++ 11 ব্যবহার করা যেতে পারে।
    • দ্বিতীয় পাঠযোগ্য আইএমও।
  • শেষ, ফাংশন প্যারামিটারে:

    • এটি প্রাক-সি ++ 11 ব্যবহার করা যেতে পারে।
    • এটি কনস্ট্রাক্টরগুলিতে ব্যবহারযোগ্য।
    • এটি ব্যবহারকারী-সংজ্ঞায়িত রূপান্তর অপারেটরে ব্যবহার করা যাবে না। (কোনও পরামিতি নেই)
    • এটা তোলে আর্গুমেন্ট নির্দিষ্ট সংখ্যা দিয়ে পদ্ধতিতে ব্যবহার করা যাবে না (ইউনারী / বাইনারি অপারেটর +, -, *, ...)
    • এটি উত্তরাধিকার হিসাবে নিরাপদে ব্যবহার করা যেতে পারে (নীচে দেখুন)।
    • ফাংশনের স্বাক্ষর পরিবর্তন করুন (আপনার কাছে শেষ যুক্তি হিসাবে মূলত অতিরিক্ত রয়েছে void* = nullptr) (সুতরাং ফাংশন পয়েন্টারটি পৃথক হতে পারে এবং অন্যভাবে)

সদস্য এবং অ-সদস্য ফাংশন টেম্পলেটগুলির জন্য কোনও পার্থক্য রয়েছে?

উত্তরাধিকারের সাথে সূক্ষ্ম পার্থক্য রয়েছে এবং using:

মতে using-declarator(জোর খনি):

namespace.udecl

ব্যাবহারকারী-ঘোষক দ্বারা প্রবর্তিত সংস্থার সেটটি বর্ণনাকারী হিসাবে লুকানো রয়েছে এমন ফাংশনগুলি বাদ দিয়ে, ব্যবহার-ঘোষণাকারীর নামের জন্য যোগ্য নাম অনুসন্ধান ([বেসিক.লুপআপ.কুয়াল], [শ্রেণি.মেবার.লুকআপ]) সম্পাদন করে পাওয়া যায় is নিচে.

...

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

সুতরাং টেমপ্লেট আর্গুমেন্ট এবং রিটার্নের ধরণের উভয়ের জন্যই পদ্ধতিগুলি লুকানো রয়েছে নিম্নলিখিত পরিস্থিতিতে:

struct Base
{
    template <std::size_t I, std::enable_if_t<I == 0>* = nullptr>
    void f() {}

    template <std::size_t I>
    std::enable_if_t<I == 0> g() {}
};

struct S : Base
{
    using Base::f; // Useless, f<0> is still hidden
    using Base::g; // Useless, g<0> is still hidden

    template <std::size_t I, std::enable_if_t<I == 1>* = nullptr>
    void f() {}

    template <std::size_t I>
    std::enable_if_t<I == 1> g() {}
};

ডেমো (জিসিসি ভুলভাবে বেস ফাংশনটি সন্ধান করে)।

যুক্তি সহ, একই ধরণের দৃশ্য কাজ করে:

struct Base
{
    template <std::size_t I>
    void h(std::enable_if_t<I == 0>* = nullptr) {}
};

struct S : Base
{
    using Base::h; // Base::h<0> is visible

    template <std::size_t I>
    void h(std::enable_if_t<I == 1>* = nullptr) {}
};

ডেমো

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