সি ++ এ SFINAE কার্য করার পদ্ধতিগুলি


40

আমি একটি প্রকল্পে ভারী SFINAE ফাংশনটি ব্যবহার করছি এবং নিম্নলিখিত দুটি পদ্ধতির (শৈলী ব্যতীত) মধ্যে কোনও পার্থক্য আছে কিনা তা নিশ্চিত নই:

#include <cstdlib>
#include <type_traits>
#include <iostream>

template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
    std::cout << "method 1" << std::endl;
}

template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
    std::cout << "method 2" << std::endl;
}

int main()
{
    foo<int>();
    foo<double>();

    std::cout << "Done...";
    std::getchar();

    return EXIT_SUCCESS;
}

প্রোগ্রাম আউটপুট প্রত্যাশিত হিসাবে:

method 1
method 2
Done...

আমি স্ট্যাকওভারফ্লোতে প্রায়শই ব্যবহার করা পদ্ধতি 2 দেখেছি তবে আমি পদ্ধতিটি 1 পছন্দ করি।

এই দুটি পদ্ধতির মধ্যে পার্থক্য থাকলে কি কোনও পরিস্থিতি রয়েছে?


আপনি এই প্রোগ্রামটি কীভাবে চালাবেন? এটি আমার জন্য সংকলন করবে না
আইজেল

@ এল্টার ইগেল এর জন্য একটি সি ++ 17 সংকলক লাগবে। এই উদাহরণটি পরীক্ষা করতে আমি এমএসভিসি 2019 ব্যবহার করেছি তবে আমি মূলত কলংয়ের সাথে কাজ করি।
কিথ

সম্পর্কিত: কেন-আমি-এড়ানো-স্টেনেবল-যদি-ইন-ফাংশন-স্বাক্ষরসমূহ এবং সি ++ 20 ধারণা সহ নতুন উপায়গুলি উপস্থাপন করে :-)
জারোড 42

@ জারোড 42 কনসেপ্টগুলি সি ++ 20 থেকে আমার জন্য সবচেয়ে প্রয়োজনীয় জিনিসগুলির মধ্যে একটি।
বলেছেন মনিকা পুনরায় স্থাপন করুন

উত্তর:


35

আমি স্ট্যাকওভারফ্লোতে প্রায়শই ব্যবহার করা পদ্ধতি 2 দেখেছি তবে আমি পদ্ধতিটি 1 পছন্দ করি।

পরামর্শ: পদ্ধতি 2 পছন্দ করুন।

উভয় পদ্ধতি একক ফাংশন নিয়ে কাজ করে। সমস্যাটি তখন দেখা দেয় যখন আপনার একই ফাংশনটির চেয়ে বেশি একই স্বাক্ষর সহ, এবং আপনি সেটটির কেবলমাত্র একটি ফাংশন সক্ষম করতে চান।

ধরুন, আপনি সক্ষম চান foo(), সংস্করণ 1, যখন bar<T>()(সাজা এটি একটি এর constexprফাংশন) হয় true, এবং foo(), সংস্করণ 2, যখন bar<T>()হয় false

সঙ্গে

template <typename T, typename = std::enable_if_t<true == bar<T>()>>
void foo () // version 1
 { }

template <typename T, typename = std::enable_if_t<false == bar<T>()>>
void foo () // version 2
 { }

আপনি একটি সংকলন ত্রুটি পেয়েছেন কারণ আপনার দ্ব্যর্থহীনতা: foo()একই স্বাক্ষরযুক্ত দুটি ফাংশন (একটি ডিফল্ট টেম্পলেট প্যারামিটার স্বাক্ষর পরিবর্তন করে না)।

তবে নিম্নলিখিত সমাধান

template <typename T, std::enable_if_t<true == bar<T>(), bool> = true>
void foo () // version 1
 { }

template <typename T, std::enable_if_t<false == bar<T>(), bool> = true>
void foo () // version 2
 { }

কাজ করে, কারণ SFINAE ফাংশনের স্বাক্ষরটি সংশোধন করে।

সম্পর্কিত নয় এমন পর্যবেক্ষণ: তৃতীয় পদ্ধতিও রয়েছে: রিটার্নের প্রকারটি সক্ষম / অক্ষম করুন (শ্রেণি / কাঠামো নির্মাণকারী বাদে স্পষ্টতই)

template <typename T>
std::enable_if_t<true == bar<T>()> foo () // version 1
 { }

template <typename T>
std::enable_if_t<false == bar<T>()> foo () // version 2
 { }

পদ্ধতি 2 হিসাবে, পদ্ধতি 3 একই স্বাক্ষরযুক্ত বিকল্প ফাংশন নির্বাচনের সাথে সামঞ্জস্যপূর্ণ।


1
দুর্দান্ত ব্যাখ্যার জন্য ধন্যবাদ, আমি এখন থেকে 2: 3 পদ্ধতি পছন্দ করব :-)
কিথ

"একটি ডিফল্ট টেম্পলেট প্যারামিটার স্বাক্ষর পরিবর্তন করে না" - এটি আপনার দ্বিতীয় বৈকল্পিকের মধ্যে কীভাবে আলাদা, এটি ডিফল্ট টেম্পলেট প্যারামিটারগুলিও ব্যবহার করে?
এরিক

1
@ এরিক - বলা সহজ নয় ... আমি মনে করি যে অন্য উত্তরগুলি এটি আরও ভালভাবে ব্যাখ্যা করেছে ... যদি SFINAE ডিফল্ট টেম্পলেট যুক্তিটি সক্ষম / অক্ষম করে, foo()আপনি স্পষ্ট দ্বিতীয় টেম্পলেট প্যারামিটার ( foo<double, double>();কল) দিয়ে কল করলে ফাংশনটি উপলব্ধ থাকে । এবং যদি উপলভ্য থাকে, তবে অন্যান্য সংস্করণটির সাথে একটি অস্পষ্টতা রয়েছে। পদ্ধতি 2 দিয়ে, SFINAE ডিফল্ট প্যারামিটার নয়, দ্বিতীয় তর্কটি সক্ষম / অক্ষম করে। সুতরাং আপনি এটিকে প্যারামিটারের বর্ণনামূলক বলতে পারেন না কারণ একটি বিকল্প ব্যর্থতা রয়েছে যা দ্বিতীয় প্যারামিটারের অনুমতি দেয় না। সুতরাং সংস্করণটি অনুপলব্ধ, সুতরাং কোনও
দ্বিধা

3
পদ্ধতি 3টির প্রতীক-নামটিতে সাধারণত ফাঁস না হওয়ার অতিরিক্ত সুবিধা রয়েছে। auto foo() -> std::enable_if_t<...>ফাংশন-স্বাক্ষরটি আড়াল করতে এবং ফাংশন-আর্গুমেন্টগুলি ব্যবহার করার অনুমতি দেওয়ার জন্য বৈকল্পিকটি প্রায়শই কার্যকর।
উত্সাহকটি

@ ম্যাক্স 66: সুতরাং মূল বিষয়টি হ'ল প্যারামিটার সরবরাহ করা হয় এবং কোনও ডিফল্ট প্রয়োজন না হলে কোনও টেম্পলেট প্যারামিটার ডিফল্টে প্রতিস্থাপন ব্যর্থতা ত্রুটি নয়?
এরিক

21

ম্যাক্স 66 এর উত্তর ছাড়াও , পদ্ধতি 2 টি পছন্দ করার আরেকটি কারণ হ'ল পদ্ধতি 1 এর সাহায্যে আপনি (দুর্ঘটনাক্রমে) দ্বিতীয় টেমপ্লেট আর্গুমেন্ট হিসাবে একটি সুস্পষ্ট টাইপের পরামিতিটি পাস করতে পারেন এবং SFINAE প্রক্রিয়াটিকে সম্পূর্ণ পরাস্ত করতে পারেন। এটি টাইপো, অনুলিপি / পেস্ট ত্রুটি বা বৃহত্তর টেম্পলেট পদ্ধতিতে তদারকি হিসাবে ঘটতে পারে।

#include <cstdlib>
#include <type_traits>
#include <iostream>

// NOTE: foo should only accept T=int
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo(){
    std::cout << "method 1" << std::endl;
}

int main(){

    // works fine
    foo<int>();

    // ERROR: subsitution failure, as expected
    // foo<double>();

    // Oops! also works, even though T != int :(
    foo<double, double>();

    return 0;
}

এখানে লাইভ ডেমো


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